Andy Armstrong provides new serial layer and refactors magellan to work with it.
authorrobertl <robertl@f51c46e8-681c-474f-0cfe-069cfd0219fb>
Thu, 13 Jul 2006 20:33:10 +0000 (20:33 +0000)
committerrobertl <robertl@f51c46e8-681c-474f-0cfe-069cfd0219fb>
Thu, 13 Jul 2006 20:33:10 +0000 (20:33 +0000)
git-svn-id: http://gpsbabel.googlecode.com/svn/trunk@2231 f51c46e8-681c-474f-0cfe-069cfd0219fb

14 files changed:
gpsbabel/Makefile.in
gpsbabel/gbser.h
gpsbabel/gbser_posix.c
gpsbabel/gbser_win.c
gpsbabel/gpsutil.c
gpsbabel/jeeps/gpsserial.c
gpsbabel/magproto.c
gpsbabel/msvc/GPSBabel.sln
gpsbabel/msvc/GPSBabel.vcproj
gpsbabel/reference/wbt-200.bin [new file with mode: 0644]
gpsbabel/reference/wbt-200.gpx [new file with mode: 0644]
gpsbabel/testo
gpsbabel/vecs.c
gpsbabel/wbt-200.c

index b2489749e0abcc82eb1a1ebde43596b7b19c40ce..16b938abec29c725cf3c4b8a87323f35f81e5a98 100644 (file)
@@ -73,7 +73,7 @@ SHAPE=shapelib/shpopen.o shapelib/dbfopen.o
 LIBOBJS = queue.o route.o waypt.o filter_vecs.o util.o vecs.o mkshort.o \
           csv_util.o strptime.o grtcirc.o vmem.o util_crc.o xmlgeneric.o \
           uuid.o formspec.o xmltag.o cet.o cet_util.o fatal.o rgbcolors.o \
-         inifile.o garmin_fs.o gbsleep.o units.o textfile.o @GBSER@ \
+         inifile.o garmin_fs.o gbsleep.o units.o textfile.o @GBSER@ gbser.o \
        $(COLDSYNC) $(GARMIN) $(JEEPS) $(SHAPE) $(FMTS) $(FILTERS)
 OBJS = main.o globals.o $(LIBOBJS)
 
index 44cf3f787af4418691306a3d6b2d7872a2634047..550c5313dba68a86321744a30ed6076d92b28973 100644 (file)
 
  */
 
-void *gbser_init(const char *name);
-void gbser_deinit (void *);
-int  gbser_read(void *handle, char *ibuf, int sz);
-int  gbser_setspeed(void *handle, unsigned speed);
+#ifndef __GBSER_H
+#define __GBSER_H
 
-#if __WIN32__
-char * fix_win_serial_name(const char *comname);
+#define gbser_OK         0
+#define gbser_NOTHING   -1
+#define gbser_TIMEOUT   -2
+#define gbser_ERROR     -3
+
+#if defined(__WIN32__) || defined(__CYGWIN__)
+#define WINSERIAL 1
+#else
+#define POSIXSERIAL 1
 #endif
+
+/* Open a serial port. |port_name| is the (platform specific) name
+ * of the serial device to open. Under WIN32 familiar DOS port names
+ * ('com1:') are translated into the equivalent name required by
+ * WIN32
+ */
+void *gbser_init(const char *port_name);
+
+/* Close a serial port
+ */
+void gbser_deinit(void *handle);
+
+/* Set the serial port speed.
+ */
+int gbser_set_speed(void *handle, unsigned speed);
+
+/* Set the serial port speed, start, parity and stop bits */
+int gbser_set_port(void *handle, unsigned speed, 
+                                 unsigned bits, 
+                                 unsigned parity, 
+                                 unsigned stop);
+
+/* Set the serial port up by parsing the supplied parameter string.
+ * Valid parameter strings look like '4800,8,N,1'. Parsing is case-
+ * insensitive, spaces are allowed around the commas and omitted
+ * trailing fields will default to '8', 'N' and '1'
+ */
+int gbser_setup(void *handle, const char *spec);
+
+/* Return true if there are characters available on the serial port
+ */
+int gbser_avail(void *handle);
+
+/* Read as many bytes as are available without blocking. At most |len|
+ * bytes will be read. Returns the number of bytes read or gbser_ERROR if an
+ * error occurs.
+ */
+int gbser_read(void *handle, void *buf, unsigned len);
+
+/* Read the specified number of bytes. Block until the requested number
+ * of bytes have been read or the timeout (in ms) is exceeded.
+ */
+int gbser_read_wait(void *handle, void *buf, unsigned len, unsigned ms);
+
+/* Read from the serial port until the specified |eol| character is
+ * found. Any character matching |discard| will be discarded. To
+ * read lines terminated by 0x0A0x0D discarding linefeeds use
+ * gbser_read_line(h, buf, len, 1000, 0x0D, 0x0A);
+ */
+int gbser_read_line(void *handle, void *buf, 
+                    unsigned len, unsigned ms,
+                    int eol, int discard);
+
+/* Read a single character from the port, returning immediately if
+ * none are available. TODO: Define return values
+ */
+int gbser_readc(void *handle);
+
+/* Read a single character from the port, waiting up to |ms|
+ * milliseconds for a character to be available.
+ */
+int gbser_readc_wait(void *handle, unsigned ms);
+
+/* Discard any pending input on the serial port.
+ */                    
+int gbser_flush(void *handle);
+
+/* Write |len| bytes from |buf| to the serial port.
+ */
+int gbser_write(void *handle, const void *buf, unsigned len);
+
+/* Write a null terminated string in |str| to the serial
+ * port.
+ */
+int gbser_print(void *handle, const char *str);
+
+/* Write a single character to the serial port.
+ */ 
+int gbset_writec(void *handle, int c);
+
+/* Return true if a port name seems to refer to a serial port.
+ * On Windows this tests the filename (against the regex
+ * /^(\\\\\.\\\\)?com\d+:?$/i). On Posix it returns the value of
+ * isatty()
+ */
+int gbser_is_serial(const char *port_name);
+
+/* This isn't part of the above abstraction; it's just a helper for 
+ * the other serial modules in the tree.
+ *
+ * Windows does a weird thing with serial ports.  
+ * COM ports 1 - 9 are "COM1:" through "COM9:"
+ * The one after that is \\.\\com10 - this function tries to plaster over
+ * that.
+ * It returns a pointer to a staticly allocated buffer and is therefore not
+ * thread safe.   The buffer pointed to remains valid only until the next
+ * call to this function.
+ */
+
+const char *fix_win_serial_name_r(const char *comname, char *obuf, size_t len);
+const char *fix_win_serial_name(const char *comname);
+
+#endif /* GBSER_H */
index 0a54605ddb25b518038cea5b1f0c38c6575272fe..8a81862975fee6686ca3f9fb866a931561ce6295 100644 (file)
@@ -1,6 +1,6 @@
 /*
-    Serial interface - POSIX layer.
-
+    Serial interface for POSIX tty handling.
+    
     Copyright (C) 2006  Robert Lipe, robertlipe@usa.net
 
     This program is free software; you can redistribute it and/or modify
 
 #include "defs.h"
 #include "gbser.h"
-#include <assert.h>
+#include "gbser_private.h"
+
+#include <sys/time.h>
 #include <termios.h>
 #include <unistd.h>
 #include <fcntl.h>
+#include <errno.h>
+
+#include <assert.h>
+#include <stdarg.h>
 
-#define MYMAGIC 0x91827364
 typedef struct {
-       struct termios orig_tio;
-       struct termios my_tio;
-       FILE *fh;
-       int fd;
-       unsigned long magic;
-} gbser_posix_handle;
-
-static 
-speed_t
-mkspeed(unsigned br)
-{
-        switch (br) {
-                case 1200: return B1200;
-                case 2400: return B2400;
-                case 4800: return B4800;
-                case 9600: return B9600;
-                case 19200: return B19200;
+       struct termios  old_tio;
+       struct termios  new_tio;
+       int             fd;
+       unsigned        vmin, vtime;
+       unsigned long   magic;
+
+       unsigned char   inbuf[BUFSIZE];
+       unsigned        inbuf_used;
+} gbser_handle;
+
+/* Wrapper to safely cast a void * into a gbser_handle */
+static gbser_handle *gbser__get_handle(void *p) {
+    gbser_handle *h = (gbser_handle *) p;
+       assert(h->magic == MYMAGIC);
+    return h;
+}
+
+static speed_t mkspeed(unsigned br) {
+    switch (br) {
+    case   1200: return  B1200;
+    case   2400: return  B2400;
+    case   4800: return  B4800;
+    case   9600: return  B9600;
+    case  19200: return  B19200;
 #if defined B57600
-                case 57600: return B57600;
+    case  57600: return  B57600;
 #endif
 #if defined B115200
-                case 115200: return B115200;
+    case 115200: return B115200;
 #endif
-                default: return B4800;
-        }
+    default:
+        fatal("Unsupported serial speed: %d\n", br);
+        return 0;   /* keep compiler happy */
+    }
 }
 
-/*
-gbser_istty(void *handle)
-{
-       gbser_posix_handle *h = (gbser_posix_handle *) handle;
-       assert(h->magic == MYMAGIC);
+typedef struct timeval hp_time;
 
-       return isatty(h->fd);
+static void get_time(hp_time *tv) {
+    gettimeofday(tv, NULL);
 }
-*/
 
-void *
-gbser_init(const char *name)
-{
-       gbser_posix_handle *h;
+static double elapsed(hp_time *tv) {
+    hp_time now;
+    double ot = (double) tv->tv_sec  * 1000 +
+                (double) tv->tv_usec / 1000;
+    double nt;
+    gettimeofday(&now, NULL);
+    nt = (double) now.tv_sec  * 1000 + 
+         (double) now.tv_usec / 1000;
+    /*printf("elapsed -> %f\n", nt - ot);*/
+    return nt - ot;
+}
+
+static int set_rx_timeout(gbser_handle *h, unsigned vmin, unsigned vtime) {
+    if (vmin  > 255) { vmin  = 255; }
+    if (vtime > 255) { vtime = 255; }
+    if (vmin != h->vmin || vtime != h->vtime) {
+        h->vmin  = h->new_tio.c_cc[VMIN]  = vmin;
+        h->vtime = h->new_tio.c_cc[VTIME] = vtime;
+
+        /*printf("VMIN=%d, VTIME=%d\n", h->vmin, h->vtime);*/
+
+       return tcsetattr(h->fd, TCSANOW, &h->new_tio) ? gbser_ERROR : gbser_OK;
+    } else {
+        return 0;
+    }
+}
+
+/* Open a serial port. |port_name| is the (platform specific) name
+ * of the serial device to open. Under WIN32 familiar DOS port names
+ * ('com1:') are translated into the equivalent name required by
+ * WIN32
+ */
+void *gbser_init(const char *port_name) {
+       gbser_handle *h;
+
+       gbser__db(4, "gbser_init(\"%s\")\n", port_name);
 
        h = xcalloc(sizeof *h, 1);
        h->magic = MYMAGIC;
+       h->vmin = h->vtime = 0;
+
+    if (h->fd = open(port_name, O_RDWR | O_NOCTTY), h->fd == -1) {
+               gbser__db(1, "Failed to open port (%s)\n", strerror(errno));    
+        goto failed;
+    }
 
-       h->fh = xfopen(name,  "rb", "serial layer");
-       h->fd = fileno(h->fh);
-// h->fd = open(name, O_RDWR | O_EXCL | O_SYNC);
-       
        if (!isatty(h->fd)) { 
-               goto open_fail;
+               gbser__db(1, "%s is not a TTY\n");
+               goto failed;
        }
 
-       tcgetattr(h->fd, &h->orig_tio);
-
-       h->my_tio = h->orig_tio;
-       h->my_tio.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|
-               IGNCR|IGNCR|IXON);
-        h->my_tio.c_cflag &= ~(CSIZE|PARENB);
-        h->my_tio.c_cflag |= CS8;
-        h->my_tio.c_oflag = 0;
-        h->my_tio.c_lflag = 0;
-        h->my_tio.c_iflag = 0;
-        h->my_tio.c_cc[VTIME] = 10;    /* Time out after one second */
-        h->my_tio.c_cc[VMIN] = 255;
+       if (gbser_set_port(h, 4800, 8, 0, 1)) {
+               gbser__db(1, "gbser_set_port() failed\n");
+           goto failed;
+       }
 
        return h;
 
-open_fail:
-       if (h->fh) {
-               fclose(h->fh);
+failed:
+       if (h->fd != -1) {
+           close(h->fd);
        }
+       
        xfree(h);
+       
        return NULL;
 }
 
-void 
-gbser_deinit(void *handle)
-{
-       gbser_posix_handle *h = (gbser_posix_handle *) handle;
-       assert(h->magic == MYMAGIC);
+/* Close a serial port
+ */
+void gbser_deinit(void *handle) {
+       gbser_handle *h = gbser__get_handle(handle);
+
+       tcsetattr(h->fd, TCSAFLUSH, &h->old_tio);
+       close(h->fd);
 
-       tcsetattr(h->fd, TCSAFLUSH, &h->orig_tio);
-       fclose(h->fh);
-       xfree(handle);
+       xfree(h);
 }
 
-int
-gbser_setspeed(void *handle, unsigned speed)
-{
+int gbser_set_port(void *handle, unsigned speed, unsigned bits, unsigned parity, unsigned stop) {
+       gbser_handle *h = gbser__get_handle(handle);
        speed_t s;
-       gbser_posix_handle *h = (gbser_posix_handle *) handle;
-       assert(h->magic == MYMAGIC);
+       
+       static unsigned bit_flags[] = {
+               0, 0, 0, 0, 0, CS5, CS6, CS7, CS8
+       };
+
+    if (bits < 5 || bits > 8) {
+        fatal("Unsupported bits setting: %d\n", bits);
+    }
+
+    if (parity > 2) {
+        fatal("Unsupported parity setting: %d\n", parity);
+    }
+    
+    if (stop < 1 || stop > 2) {
+        fatal("Unsupported stop setting: %d\n", stop);
+    }
 
        s = mkspeed(speed);
 
-       cfsetospeed(&h->my_tio, s);
-       cfsetispeed(&h->my_tio, s);
+       /* TODO: We don't /fully/ initialise the port's stat here... */
+
+       tcgetattr(h->fd, &h->old_tio);
+
+       h->new_tio = h->old_tio;
+
+       /* clear bits */
+       cfmakeraw(&h->new_tio);
+
+       h->new_tio.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP |
+                                       INLCR  | IGNCR  | IXON);
+       h->new_tio.c_cflag &= ~(CSIZE  | PARENB | PARODD | CSTOPB);
+    
+       /* set data bits, */
+       h->new_tio.c_cflag |= bit_flags[bits];
+
+       /* stop bits and... */
+       if (stop == 2) {
+               h->new_tio.c_cflag |= CSTOPB;
+       }
+    
+       /* parity */
+       if (parity != 0) {
+               h->new_tio.c_cflag |= PARENB;
+               if (parity == 1) {
+                       h->new_tio.c_cflag |= PARODD;
+               }
+       }
+    
+       h->new_tio.c_oflag = 0;
+       h->new_tio.c_lflag = 0;
 
-       return !tcsetattr(h->fd, TCSAFLUSH, &h->my_tio);
+       h->new_tio.c_cc[VMIN]  = h->vmin;
+       h->new_tio.c_cc[VTIME] = h->vtime;
+
+       cfsetospeed(&h->new_tio, s);
+       cfsetispeed(&h->new_tio, s);
+
+       return tcsetattr(h->fd, TCSADRAIN, &h->new_tio) ? gbser_ERROR : gbser_OK;
+}
+
+unsigned gbser__read_buffer(void *handle, void **buf, unsigned *len) {
+       gbser_handle *h = gbser__get_handle(handle);
+    unsigned count = *len;
+    unsigned char *cp = *buf;
+    if (count > h->inbuf_used) {
+        count = h->inbuf_used;
+    }
+    
+    memcpy(cp, h->inbuf, count);
+    memmove(h->inbuf, h->inbuf + count, 
+                      h->inbuf_used - count);
+    h->inbuf_used -= count;
+    *len -= count;
+    cp   += count;
+    *buf = (void *) cp;
+    return count;
 }
 
-int
-gbser_read(void *handle, char *ibuf, int size)
-{
-       gbser_posix_handle *h = (gbser_posix_handle *) handle;
+/* Return when the input buffer contains at least |want| bytes or |*ms|
+ * milliseconds have elapsed. |ms| may be NULL or |*ms| may be zero to
+ * poll the port for available bytes and return immediately. |*ms| will
+ * be updated to indicate the remaining time on exit.
+ * Returns the number of bytes available (>=0) or an error code (<0).
+ */
+int gbser__fill_buffer(void *handle, unsigned want, unsigned *ms) {
+    int rc;
+       gbser_handle *h = gbser__get_handle(handle);
+    
+    if (want > BUFSIZE) {
+        want = BUFSIZE;
+    }
 
-       ibuf[0] = 0;
-       assert(h->magic == MYMAGIC);
+    /* Already got enough bytes? */
+    if (h->inbuf_used >= want) {
+        return h->inbuf_used;
+    }
+
+    if (NULL == ms || 0 == *ms) {
+        if ((rc = set_rx_timeout(h, 0, 0), rc < 0) ||
+            (rc = read(h->fd, h->inbuf + h->inbuf_used, 
+                              want - h->inbuf_used), rc < 0)) {
+            return gbser_ERROR;
+        }
+        h->inbuf_used += rc;
+        /*printf("Got %d bytes\n", rc);*/
+    } else {
+        hp_time tv;
+        get_time(&tv);
+        double time_left = *ms;
+        
+        for (;;) {
+            fd_set rec;
+            struct timeval t;
+            
+            time_left = *ms - elapsed(&tv);
+            if (time_left <= 0 || h->inbuf_used >= want) {
+                break;
+            }
+
+            FD_ZERO(&rec);
+            FD_SET(h->fd, &rec);
+
+            t.tv_sec  = (time_t) time_left / 1000;
+            t.tv_usec = (suseconds_t) ((unsigned) time_left % 1000) * 1000;
+
+            if (select(h->fd + 1, &rec, NULL, NULL, &t) < 0) {
+                return gbser_ERROR;
+            }
+
+            time_left = *ms - elapsed(&tv);
+        
+            if (FD_ISSET(h->fd, &rec)) {
+                unsigned vmin = 0, vtime = 0;
+                if (time_left >= 100) {
+                    vmin  = want - h->inbuf_used;
+                    vtime = (unsigned) time_left / 100;
+                }
+                if ((rc = set_rx_timeout(h, vmin, vtime), rc < 0) ||
+                    (rc = read(h->fd, h->inbuf + h->inbuf_used, 
+                                      want - h->inbuf_used), rc < 0)) {
+                    return gbser_ERROR;
+                }
+                h->inbuf_used += rc;
+                /*printf("Got %d bytes\n", rc);*/
+            }
+        }
+        *ms = (time_left < 0) ? 0 : time_left;
+    }
+
+    return h->inbuf_used;
+}
+
+/* Discard any pending input on the serial port.
+ */                    
+int gbser_flush(void *handle) {
+       gbser_handle *h = gbser__get_handle(handle);
+       h->inbuf_used = 0;
+    if (tcflush(h->fd, TCIFLUSH)) {
+        return gbser_ERROR;
+    }
+
+    return gbser_OK;
+}
+
+/* Write |len| bytes from |buf| to the serial port.
+ */
+int gbser_write(void *handle, const void *buf, unsigned len) {
+       gbser_handle *h = gbser__get_handle(handle);
+    const char *bp = buf;
+    int rc;
+    while (len > 0) {
+        /*printf("write(%d, %p, %d)\n", h->fd, bp, len);*/
+        if (rc = write(h->fd, bp, len), rc < 0) {
+            printf("rc = %d, errno = %d (%s)\n", rc, errno, strerror(errno));
+            return gbser_ERROR;
+        }
+        len -= rc;
+        bp  += rc;
+    }
+    return gbser_OK;
+}
+
+/* Return true if a port name seems to refer to a serial port.
+ * On Windows this tests the filename (against the regex
+ * /^(\\\\\.\\\\)?com\d+:?$/i). On Posix it returns the value of
+ * isatty()
+ */
+
+int gbser_is_serial(const char *port_name) {
+       int fd;
+       int is_port = 0;
+
+    if (fd = open(port_name, O_RDWR | O_NOCTTY), fd == -1) {
+               gbser__db(1, "Failed to open port (%s) to check its type\n", strerror(errno));
+               return 0;
+    }
 
-//     n = read(h->fd, ibuf, size);
-// printf("Returning %d\n", n);
-redo:
-       fgets(ibuf, size, h->fh);
-// rtrim(ibuf);
-if (strlen(ibuf) == 0)
-  goto redo;
-       return 1;
+       is_port = isatty(fd);
+
+       close(fd);
+
+       return is_port;
+}
+
+/* This isn't part of the above abstraction; it's just a helper for 
+ * the other serial modules in the tree.
+ *
+ * Windows does a weird thing with serial ports.  
+ * COM ports 1 - 9 are "COM1:" through "COM9:"
+ * The one after that is \\.\\com10 - this function tries to plaster over
+ * that.
+ * It returns a pointer to a staticly allocated buffer and is therefore not
+ * thread safe.   The buffer pointed to remains valid only until the next
+ * call to this function.
+ */
+
+const char *fix_win_serial_name_r(const char *comname, char *obuf, size_t len) {
+    strncpy(obuf, comname, len);
+    return obuf;
+}
+
+static char gb_com_buffer[100];
+
+const char *fix_win_serial_name(const char *comname) {
+       return fix_win_serial_name_r(comname, gb_com_buffer, sizeof(gb_com_buffer));
+}
+
+/* Read from the serial port until the specified |eol| character is
+ * found. Any character matching |discard| will be discarded. To
+ * read lines terminated by 0x0A0x0D discarding linefeeds use
+ * gbser_read_line(h, buf, len, 1000, 0x0D, 0x0A);
+ */
+int gbser_read_line(void *handle, void *buf, 
+                    unsigned len, unsigned ms,
+                    int eol, int discard) {
+    char *bp = buf;
+    unsigned pos = 0;
+    hp_time tv;
+    get_time(&tv);
+    bp[pos] = '\0';
+    for (;;) {
+        unsigned time_left = ms - elapsed(&tv);
+        int c;
+        if (time_left <= 0) {
+            return gbser_TIMEOUT;
+        }
+        c = gbser_readc_wait(handle, time_left);
+        if (c == gbser_ERROR) {
+            return c;
+        } else if (c == eol) {
+            return gbser_OK;
+        }
+        if (c != gbser_NOTHING && c != discard && pos < len - 1) {
+            bp[pos++] = c;
+            bp[pos]   = '\0';
+        }
+    }
 }
index c113614ff0b9c8fd9781dd7a1071ba149c00e560..a339cf86a6013fb7206704e6a70c883831504a51 100644 (file)
@@ -1,6 +1,6 @@
 /*
     Serial interface - Windows layer.
-
+    
     Copyright (C) 2006  Robert Lipe, robertlipe@usa.net
 
     This program is free software; you can redistribute it and/or modify
 
 #include "defs.h"
 #include "gbser.h"
+#include "gbser_private.h"
+
 #include <windows.h>
 #include <setupapi.h>
 
-typedef struct gbser_win_handle {
-       HANDLE comport;
-} gbser_win_handle;
+#include <assert.h>
+#include <stdarg.h>
 
-#define xCloseHandle(a) if (a) { CloseHandle(a); } a = NULL;
+typedef struct {
+       HANDLE          comport;
+       DWORD           timeout;
+       unsigned long   magic;
 
-void *
-gbser_init(const char *portname)
-{
-//     DCB tio;        
-       COMMTIMEOUTS timeout;
-       HANDLE comport;
-//     char *xname= xstrdup("\\\\.\\\\");
-       char *xname = fix_win_serial_name(portname);
-       gbser_win_handle* handle = xcalloc(1, sizeof(*handle));;
-
-//     /* Amazingly, windows will fail the open below unless we
-//      * prepend \\.\ to the name.   It also then fails the open
-//      * unless we strip the colon from the name.  Aaaaargh!
-//      */
-//     xname = xstrappend(xname, portname);
-//     if (xname[strlen(xname)-1] == ':')
-//             xname[strlen(xname)-1] = 0;
-//     xCloseHandle(comport);
-
-       comport = CreateFile(xname, GENERIC_READ|GENERIC_WRITE, 
-                               0, NULL, OPEN_EXISTING, 0, NULL);
+       unsigned char   inbuf[BUFSIZE];
+       unsigned        inbuf_used;
+} gbser_handle;
 
-       if (comport == INVALID_HANDLE_VALUE) {
-               return NULL;
-       }
-       handle->comport = comport;
+#define DEV_PREFIX "\\\\.\\\\"
 
-#if 0
-       tio.DCBlength = sizeof(DCB);
-       GetCommState (comport, &tio);
-//     tio.BaudRate = mkspeed(bitrate);
-       {
-       extern int mkspeed(int);
-       tio.BaudRate = mkspeed(4800);
-       }
-       tio.fBinary = TRUE;
-       tio.fParity = TRUE;
-       tio.fOutxCtsFlow = FALSE;
-       tio.fOutxDsrFlow = FALSE;
-       tio.fDtrControl = DTR_CONTROL_ENABLE;
-       tio.fDsrSensitivity = FALSE;
-       tio.fTXContinueOnXoff = TRUE;
-       tio.fOutX = FALSE;
-       tio.fInX = FALSE;
-       tio.fErrorChar = FALSE;
-       tio.fNull = FALSE;
-       tio.fRtsControl = RTS_CONTROL_ENABLE;
-       tio.fAbortOnError = FALSE;
-       tio.ByteSize = 8;
-       tio.Parity = NOPARITY;
-       tio.StopBits = ONESTOPBIT;
-
-       if (!SetCommState (comport, &tio)) {
-               /*
-                *  Probably not a com port.   Let caller try it as a file.
-                */
-
-               return NULL;
-       }
-#else
-       gbser_setspeed(handle, 4800);
-#endif
-
-       GetCommTimeouts (comport, &timeout);
-       /* We basically do single character reads and simulate line input
-        * mode, so these values are kind of fictional.
-        */
-       timeout.ReadIntervalTimeout = 1000;
-       timeout.ReadTotalTimeoutMultiplier = 1000;
-       timeout.ReadTotalTimeoutConstant = 1000;
-       timeout.WriteTotalTimeoutMultiplier = 1000;
-       timeout.WriteTotalTimeoutConstant = 1000;
-       if (!SetCommTimeouts (comport, &timeout)) {
-               xCloseHandle (comport);
-               fatal("SetCommTimeouts failed.\n");
-       }
-       return handle;
+/* Wrapper to safely cast a void * into a gbser_handle */
+static gbser_handle *gbser__get_handle(void *p) {
+    gbser_handle *h = (gbser_handle *) p;
+       assert(h->magic == MYMAGIC);
+    return h;
 }
 
-/* 
- * Returns 1 on success, 0 on errro.
- */
-int
-gbser_setspeed(void *handle, unsigned speed)
-{
-       extern int mkspeed(int);   /* From magproto.c  */
-       gbser_win_handle *h = (gbser_win_handle *) handle;
-       DCB tio;
-
-       tio.DCBlength = sizeof(DCB);
-       GetCommState(h->comport, &tio);
-       
-       tio.BaudRate = mkspeed(speed);
-       tio.fBinary = TRUE;
-       tio.fParity = TRUE;
-       tio.fOutxCtsFlow = FALSE;
-       tio.fOutxDsrFlow = FALSE;
-       tio.fDtrControl = DTR_CONTROL_ENABLE;
-       tio.fDsrSensitivity = FALSE;
-       tio.fTXContinueOnXoff = TRUE;
-       tio.fOutX = FALSE;
-       tio.fInX = FALSE;
-       tio.fErrorChar = FALSE;
-       tio.fNull = FALSE;
-       tio.fRtsControl = RTS_CONTROL_ENABLE;
-       tio.fAbortOnError = FALSE;
-       tio.ByteSize = 8;
-       tio.Parity = NOPARITY;
-       tio.StopBits = ONESTOPBIT;
-
-       if (!SetCommState (h->comport, &tio)) {
-               /*
-                *  Probably not a com port.   Let caller try it as a file.
-                */
-
-               return 0;
+static DWORD mkspeed(unsigned br) {
+       switch (br) {
+       case   1200: return CBR_1200;
+       case   2400: return CBR_2400;
+       case   4800: return CBR_4800;
+       case   9600: return CBR_9600;
+       case  19200: return CBR_19200;
+       case  57600: return CBR_57600;
+       case 115200: return CBR_115200;
+    default:
+        fatal("Unsupported serial speed: %d\n", br);
+        return 0;   /* keep compiler happy */
        }
-       return 1;
 }
 
-int
-gbser_read(void *handle, char *ibuf, int size)
-{
-       gbser_win_handle *h = (gbser_win_handle *) handle;
-       int i = 0;
-       DWORD cnt;
-
-        ibuf[0]='0';
-       for(;i < size;i++) {
-               if (ReadFile (h->comport, &ibuf[i], 1, &cnt, NULL) != TRUE)
-                       break;
-               if (cnt < 1)
-                       return 0;
-               if (ibuf[i] == '\n')
-                       break;
-       }
+typedef LARGE_INTEGER hp_time;
 
-       ibuf[i] = 0;
-       return 1;
+static void get_time(hp_time *tv) {
+    QueryPerformanceCounter(tv);
 }
 
-void
-gbser_deinit(void *handle)
-{
-       gbser_win_handle *h = (gbser_win_handle *) handle;
-       xfree(h);
-}
+static double elapsed(hp_time *tv) {
+    hp_time now;
+    LARGE_INTEGER tps;
 
+    QueryPerformanceFrequency(&tps);
+    QueryPerformanceCounter(&now);
 
+    return ((double) (now.QuadPart - tv->QuadPart) / 
+            (double) tps.QuadPart) * 1000;
+}
 
+static int set_rx_timeout(gbser_handle *h, DWORD timeout) {
+    if (timeout != h->timeout) {
+       COMMTIMEOUTS to;
 
-/*
- * This isn't part of the above abstraction; it's just a helper for 
+        if (!GetCommTimeouts(h->comport, &to)) {
+            return gbser_ERROR;
+        }
+    
+       to.ReadIntervalTimeout         = timeout;
+       to.ReadTotalTimeoutMultiplier  = 0;
+       to.ReadTotalTimeoutConstant    = timeout;
+       to.WriteTotalTimeoutMultiplier = 0;
+       to.WriteTotalTimeoutConstant   = 0;
+       
+       if (!SetCommTimeouts(h->comport, &to)) {
+           return gbser_ERROR;
+       } else {
+           h->timeout = timeout;
+           return gbser_OK;
+       }
+    } else {
+           return gbser_OK;
+    }
+}
+
+/* This isn't part of the above abstraction; it's just a helper for 
  * the other serial modules in the tree.
  *
  * Windows does a weird thing with serial ports.  
@@ -193,20 +115,301 @@ gbser_deinit(void *handle)
  * thread safe.   The buffer pointed to remains valid only until the next
  * call to this function.
  */
-static char gb_com_buffer[100];
 
-char *
-fix_win_serial_name(const char *comname)
-{
-       /* If in the form 'COMx:', use it in place */
-       if ((strlen(comname) == 5) && (comname[4] == ':')) {
-               strcpy(gb_com_buffer, comname);
+const char *fix_win_serial_name_r(const char *comname, char *obuf, size_t len) {
+       if (!gbser_is_serial(comname) ||
+               ((strlen(comname) == 5) && (comname[4] == ':'))) {
+               strncpy(obuf, comname, len);
        } else {
-               snprintf(gb_com_buffer, sizeof(gb_com_buffer),
-                       "\\\\.\\\\%s", comname);
-               if (gb_com_buffer[strlen(gb_com_buffer) - 1 ] == ':') {
-                       gb_com_buffer[strlen(gb_com_buffer) - 1] = 0;
+           size_t l;
+               snprintf(obuf, len, DEV_PREFIX "%s", comname);
+               l = strlen(obuf);
+               if (obuf[l - 1] == ':') {
+                       obuf[l - 1] = '\0';
                }
        }
-       return gb_com_buffer;
+
+       return obuf;
+}
+
+static char gb_com_buffer[100];
+
+const char *fix_win_serial_name(const char *comname) {
+       return fix_win_serial_name_r(comname, gb_com_buffer, sizeof(gb_com_buffer));
+}
+
+/* Open a serial port. |port_name| is the (platform specific) name
+ * of the serial device to open. Under WIN32 familiar DOS port names
+ * ('com1:') are translated into the equivalent name required by
+ * WIN32
+ */
+void *gbser_init(const char *port_name) {
+       HANDLE comport;
+       gbser_handle* h = xcalloc(1, sizeof(*h));
+    const char *xname = fix_win_serial_name(port_name);
+
+       gbser__db(2, "Translated port name: \"%s\"\n", xname);
+
+       h->magic = MYMAGIC;
+
+       comport = CreateFile(xname, GENERIC_READ | GENERIC_WRITE, 
+                                0, NULL, OPEN_EXISTING, 0, NULL);
+
+       if (comport == INVALID_HANDLE_VALUE) {
+           goto failed;
+       }
+       
+       h->comport = comport;
+       h->timeout = 1;
+
+       if (gbser_set_port(h, 4800, 8, 0, 1) || set_rx_timeout(h, 0)) {
+           goto failed;
+       }
+       
+       return h;
+       
+failed:
+       if (comport) { CloseHandle(h->comport); }
+    xfree(h);
+    
+    return NULL;
+}
+
+/* Close a serial port
+ */
+void gbser_deinit(void *handle) {
+       gbser_handle *h = gbser__get_handle(handle);
+
+    CloseHandle(h->comport);
+
+       xfree(h);
+}
+
+int gbser_set_port(void *handle, unsigned speed, unsigned bits, unsigned parity, unsigned stop) {
+       gbser_handle *h = gbser__get_handle(handle);
+       DCB tio;
+
+    if (bits < 5 || bits > 8) {
+        fatal("Unsupported bits setting: %d\n", bits);
+    }
+
+    if (parity > 2) {
+        fatal("Unsupported parity setting: %d\n", parity);
+    }
+    
+    if (stop < 1 || stop > 2) {
+        fatal("Unsupported stop setting: %d\n", stop);
+    }
+       
+       tio.DCBlength = sizeof(DCB);
+       GetCommState(h->comport, &tio);
+       
+       tio.BaudRate            = mkspeed(speed);
+       tio.fBinary             = TRUE;
+       tio.fParity             = TRUE;
+       tio.fOutxCtsFlow        = FALSE;
+       tio.fOutxDsrFlow        = FALSE;
+       tio.fDtrControl         = DTR_CONTROL_ENABLE;
+       tio.fDsrSensitivity     = FALSE;
+       tio.fTXContinueOnXoff   = TRUE;
+       tio.fOutX               = FALSE;
+       tio.fInX                = FALSE;
+       tio.fErrorChar          = FALSE;
+       tio.fNull               = FALSE;
+       tio.fRtsControl         = RTS_CONTROL_ENABLE;
+       tio.fAbortOnError       = FALSE;
+       tio.ByteSize            = bits;
+       tio.Parity              = parity == 0 ? NOPARITY : 
+                                                                 (parity == 1 ? ODDPARITY : EVENPARITY);
+       tio.StopBits            = stop   == 1 ? ONESTOPBIT : TWOSTOPBITS;
+
+       if (!SetCommState(h->comport, &tio)) {
+               return gbser_ERROR;
+       }
+       return gbser_OK;
+}
+
+unsigned gbser__read_buffer(void *handle, void **buf, unsigned *len) {
+       gbser_handle *h = gbser__get_handle(handle);
+    unsigned count = *len;
+    unsigned char *cp = *buf;
+    if (count > h->inbuf_used) {
+        count = h->inbuf_used;
+    }
+    
+    memcpy(cp, h->inbuf, count);
+    memmove(h->inbuf, h->inbuf + count, 
+                      h->inbuf_used - count);
+    h->inbuf_used -= count;
+    *len -= count;
+    cp   += count;
+    *buf = (void *) cp;
+    return count;
+}
+
+/* Return when the input buffer contains at least |want| bytes or |*ms|
+ * milliseconds have elapsed. |ms| may be NULL or |*ms| may be zero to
+ * poll the port for available bytes and return immediately. |*ms| will
+ * be updated to indicate the remaining time on exit.
+ * Returns the number of bytes available (>=0) or an error code (<0).
+ */
+int gbser__fill_buffer(void *handle, unsigned want, unsigned *ms) {
+    int rc;
+       gbser_handle *h = gbser__get_handle(handle);
+    
+    if (want > BUFSIZE) {
+        want = BUFSIZE;
+    }
+
+    /* Already got enough bytes? */
+    if (h->inbuf_used >= want) {
+        return h->inbuf_used;
+    }
+    
+    if (NULL == ms || 0 == *ms) {
+       DWORD err, nread;
+       COMSTAT stat;
+       ClearCommError(h->comport, &err, &stat);
+       if (stat.cbInQue > 0) {
+           DWORD count = want - h->inbuf_used;
+           if (count > stat.cbInQue) {
+               count = stat.cbInQue;
+           }
+           if (rc = set_rx_timeout(h, 1), rc) {
+               return rc;
+           }
+           if (!ReadFile(h->comport, h->inbuf + h->inbuf_used, 
+                                      count, &nread, NULL)) {
+               err = GetLastError();
+               if (err != ERROR_COUNTER_TIMEOUT && err != ERROR_TIMEOUT) {
+                   return gbser_ERROR;
+               }
+           }
+           h->inbuf_used += nread;
+       }
+    } else {
+        hp_time tv;
+        double time_left;
+               DWORD err, nread;
+        get_time(&tv);
+        if (rc = set_rx_timeout(h, *ms), rc) {
+            return rc;
+        }
+           if (!ReadFile(h->comport, h->inbuf + h->inbuf_used, 
+                                      want - h->inbuf_used, 
+                                      &nread, NULL)) {
+               err = GetLastError();
+               if (err != ERROR_COUNTER_TIMEOUT && err != ERROR_TIMEOUT) {
+                   return gbser_ERROR;
+               }
+           }
+           h->inbuf_used += nread;
+        time_left = *ms - elapsed(&tv);
+        *ms = time_left < 0 ? 0 : (unsigned) time_left;
+    }
+
+    return h->inbuf_used;
+}
+
+/* Discard any pending input on the serial port.
+ */                    
+int gbser_flush(void *handle) {
+       gbser_handle *h = gbser__get_handle(handle);
+       h->inbuf_used = 0;
+    if (!PurgeComm(h->comport, PURGE_RXCLEAR)) {
+        return gbser_ERROR;
+    }
+    return gbser_OK;
+}
+
+/* Write |len| bytes from |buf| to the serial port.
+ */
+int gbser_write(void *handle, const void *buf, unsigned len) {
+       gbser_handle *h = gbser__get_handle(handle);
+    DWORD nwritten;
+    const char *bp = buf;
+    /* Not sure we need to spin here - but this'll work even if we don't */
+    while (len > 0) {
+        if (!WriteFile(h->comport, bp, len, &nwritten, NULL)) {
+            return gbser_ERROR;
+        }
+        len -= nwritten;
+        bp  += nwritten;
+    }
+    return gbser_OK;
+}
+
+/* Return true if a port name seems to refer to a serial port.
+ * On Windows this tests the filename (against the regex
+ * /^(\\\\\.\\\\)?com\d+:?$/i). On Posix it returns the value of
+ * isatty()
+ */
+
+int gbser_is_serial(const char *port_name) {
+       const char *pfx = DEV_PREFIX;
+       size_t pfx_l = strlen(pfx);
+       const char *com = "COM";
+       size_t com_l = strlen(com);
+       unsigned digits;
+
+       /* Skip any prefix */
+       if (memcmp(port_name, pfx, pfx_l) == 0) {
+               port_name += pfx_l;
+       }
+
+       if (case_ignore_strncmp(port_name, com, com_l) != 0) {
+               return 0;
+       }
+
+       port_name += com_l;
+       for (digits = 0; isdigit(*port_name); port_name++, digits++) {
+               /* do nothing */
+       }
+
+       if (digits == 0) {
+               return 0;
+       }
+
+       if (*port_name == ':') {
+               port_name++;
+       }
+
+       if (*port_name != '\0') {
+               return 0;
+       }
+
+       /* Success! */
+       return 1;
+}
+
+/* Read from the serial port until the specified |eol| character is
+ * found. Any character matching |discard| will be discarded. To
+ * read lines terminated by 0x0A0x0D discarding linefeeds use
+ * gbser_read_line(h, buf, len, 1000, 0x0D, 0x0A);
+ */
+int gbser_read_line(void *handle, void *buf, 
+                    unsigned len, unsigned ms,
+                    int eol, int discard) {
+    char *bp = buf;
+    unsigned pos = 0;
+    hp_time tv;
+    get_time(&tv);
+    bp[pos] = '\0';
+    for (;;) {
+        unsigned time_left = ms - elapsed(&tv);
+        int c;
+        if (time_left <= 0) {
+            return gbser_TIMEOUT;
+        }
+        c = gbser_readc_wait(handle, time_left);
+        if (c == gbser_ERROR) {
+            return c;
+        } else if (c == eol) {
+            return gbser_OK;
+        }
+        if (c != gbser_NOTHING && c != discard && pos < len - 1) {
+            bp[pos++] = c;
+            bp[pos]   = '\0';
+        }
+    }
 }
index 9881b7d9bfad3bc3cd05888498fa0af60b33f52e..768fc5e6fb75fbb548af511a149c090afcc7a89c 100644 (file)
@@ -66,13 +66,13 @@ data_read(void)
        char icon[3] = {0};
        waypoint *wpt_tmp;
        textfile_t *tin;
-       
-       tin = textfile_init(file_in);
        /*
         * Make sure that all waypoints in single read have same 
         * timestamp.
         */
        time_t now = current_time();
+       
+       tin = textfile_init(file_in);
 
        while ((ibuf = textfile_read(tin))) {
        /*  A sharp in column zero or an blank line is a comment */
index f714cb9973e1ca7673a54b2456e5817b20be34d4..1abc30669ce26ea267be3d026cdcc07969d919f3 100644 (file)
@@ -89,7 +89,7 @@ int32 GPS_Serial_On(const char *port, gpsdevh **dh)
        DCB tio;
        COMMTIMEOUTS timeout;
        HANDLE comport;
-       char *xname = fix_win_serial_name(port);
+       const char *xname = fix_win_serial_name(port);
        win_serial_data *wsd = xcalloc(sizeof (win_serial_data), 1);
        *dh = (gpsdevh*) wsd;
 
index 18fd72115889ee0fd16b6e150fd2cd385e59410a..03bea8c1e5f6a3f7ea9093989ef2917b5afbcf33 100644 (file)
@@ -35,7 +35,7 @@ static int explorist;
 
 #define debug_serial  (global_opts.debug_level > 1)
 
-static char * termread(char *ibuf, int size);
+static char *termread(char *ibuf, int size);
 static void termwrite(char *obuf, int size);
 static void mag_readmsg(gpsdata_type objective);
 static void mag_handon(void);
@@ -77,9 +77,7 @@ typedef struct mag_rte_head {
 
 static queue rte_wpt_tmp; /* temporary PGMNWPL msgs for routes */
 
-static FILE *magfile_in;
-static FILE *magfile_out;
-static int magfd;
+static FILE *magfile_h;
 static mag_rxstate magrxstate;
 static int mag_error;
 static unsigned int last_rx_csum;
@@ -507,7 +505,7 @@ retry:
                ignore_unable = 0;
                return;
        }
-       if (IS_TKN("$PMGNCMD,END") || (is_file && (feof(magfile_in)))) {
+       if (IS_TKN("$PMGNCMD,END") || (is_file && (feof(magfile_h)))) {
                found_done = 1;
                return;
        } 
@@ -515,226 +513,65 @@ retry:
                mag_writeack(isum);
 }
 
-/* 
- * termio on Cygwin is apparently broken, so we revert to Windows serial.
- */
-#if defined (__WIN32__) || defined (__CYGWIN__)
-
-#include <windows.h>
-
-DWORD 
-mkspeed(bitrate)
-{
-       switch (bitrate) {
-               case 1200: return CBR_1200;
-               case 2400: return CBR_2400;
-               case 4800: return CBR_4800;
-               case 9600: return CBR_9600;
-               case 19200: return CBR_19200;
-               case 57600: return CBR_57600;
-               case 115200: return CBR_115200;
-               default: return CBR_4800;
-       }
-}
-
-HANDLE comport = NULL;
-
-#define xCloseHandle(a) if (a) { CloseHandle(a); } a = NULL;
-
-static
-int
-terminit(const char *portname, int create_ok)
-{
-       DCB tio;        
-       char *xname = fix_win_serial_name(portname);
-       COMMTIMEOUTS timeout;
-
-       is_file = 0;
+static void *serial_handle = NULL;
 
-       xCloseHandle(comport);
-
-       comport = CreateFile(xname, GENERIC_READ|GENERIC_WRITE, 0, NULL,
-                         OPEN_EXISTING, 0, NULL);
-       if (comport == INVALID_HANDLE_VALUE) {
-               goto try_as_file;
-       }
-       tio.DCBlength = sizeof(DCB);
-       GetCommState (comport, &tio);
-       tio.BaudRate = mkspeed(bitrate);
-       tio.fBinary = TRUE;
-       tio.fParity = TRUE;
-       tio.fOutxCtsFlow = FALSE;
-       tio.fOutxDsrFlow = FALSE;
-       tio.fDtrControl = DTR_CONTROL_ENABLE;
-       tio.fDsrSensitivity = FALSE;
-       tio.fTXContinueOnXoff = TRUE;
-       tio.fOutX = FALSE;
-       tio.fInX = FALSE;
-       tio.fErrorChar = FALSE;
-       tio.fNull = FALSE;
-       tio.fRtsControl = RTS_CONTROL_ENABLE;
-       tio.fAbortOnError = FALSE;
-       tio.ByteSize = 8;
-       tio.Parity = NOPARITY;
-       tio.StopBits = ONESTOPBIT;
-
-       if (!SetCommState (comport, &tio)) {
-               xCloseHandle(comport);
-
-               /*
-                *  Probably not a com port.   Try it as a file.
-                */
-try_as_file:
-               magfile_in = xfopen(portname, create_ok ? "w+b" : "rb", MYNAME);
+static int terminit(const char *portname, int create_ok) {
+       if (gbser_is_serial(portname)) {
+               if (serial_handle = gbser_init(portname), NULL != serial_handle) {
+                       int rc;
+                       if (rc = gbser_set_port(serial_handle, bitrate, 8, 0, 1), gbser_OK != rc) {
+                               fatal(MYNAME ": Can't configure port\n");
+                       }
+               }
+               is_file = 0;
+               return 1;
+       } else {
+               /* Does this check for an error? */
+               magfile_h = xfopen(portname, create_ok ? "w+b" : "rb", MYNAME);
                is_file = 1;
                icon_mapping = map330_icon_table;
                mag_cleanse = m330_cleanse;
                got_version = 1;
                return 0;
        }
-
-       GetCommTimeouts (comport, &timeout);
-       /* We basically do single character reads and simulate line input
-        * mode, so these values are kind of fictional.
-        */
-       timeout.ReadIntervalTimeout = 1000;
-       timeout.ReadTotalTimeoutMultiplier = 1000;
-       timeout.ReadTotalTimeoutConstant = 1000;
-       timeout.WriteTotalTimeoutMultiplier = 1000;
-       timeout.WriteTotalTimeoutConstant = 1000;
-       if (!SetCommTimeouts (comport, &timeout)) {
-               xCloseHandle (comport);
-               fatal(MYNAME ": set timeouts\n");
-       }
-       return 1;
 }
 
-static char * 
-termread(char *ibuf, int size)
-{
-       int i=0;
-       DWORD cnt;
-
+static char *termread(char *ibuf, int size) {
        if (is_file) {
-               return fgets(ibuf, size, magfile_in);
-       }
-
-       ibuf[i]='a';
-       for(;i < size;i++) {
-               if (ReadFile (comport, &ibuf[i], 1, &cnt, NULL) != TRUE)
-                       break;
-               if (cnt < 1) 
-                       return NULL;
-               if (ibuf[i] == '\n') 
-                       break;
+               return fgets(ibuf, size, magfile_h);
+       } else {
+               int rc = gbser_read_line(serial_handle, ibuf, size, 2000, '\x0a', '\x0d');
+               if (rc != gbser_OK) {
+                       fatal(MYNAME ": Read error\n");
+               }
+               return ibuf;
        }
-       ibuf[i] = 0;
-       return ibuf;
 }
 
-static void
-termwrite(char *obuf, int size)
-{
-       DWORD len;
-
+static void termwrite(char *obuf, int size) {
        if (is_file) {
-               fwrite(obuf, size, 1, magfile_out);
-               return;
-       }
-       WriteFile (comport, obuf, size, &len, NULL);
-       if ((int) len != size) {
-               fatal(MYNAME ":.  Wrote %d of %d bytes.\n", len, size);
-       }
-}
-
-static
-void
-termdeinit()
-{
-        xCloseHandle(comport);
-}
-
-#else
-
-#include <termios.h>
-#include <unistd.h>
-
-speed_t 
-mkspeed(unsigned br)
-{
-       switch (br) {
-               case 1200: return B1200;
-               case 2400: return B2400;
-               case 4800: return B4800;
-               case 9600: return B9600;
-               case 19200: return B19200;
-#if defined B57600
-               case 57600: return B57600;
-#endif
-#if defined B115200
-               case 115200: return B115200;
-#endif
-               default: return B4800;
+               size_t nw;
+               if (nw = fwrite(obuf, 1, size, magfile_h), nw < (size_t) size) {
+                       fatal(MYNAME ": Write error");
+               }
+       } else {
+               int rc;
+               if (rc = gbser_write(serial_handle, obuf, size), rc < 0) {
+                       fatal(MYNAME ": Write error");
+               }
        }
 }
 
-
-static struct termios orig_tio;
-static void
-terminit(const char *portname, int create_ok)
-{
-       struct termios new_tio;
-
-        magfile_in = xfopen(portname, "rb", MYNAME);
-
-       is_file = (0 == strcmp(portname,"-")) || !isatty(fileno(magfile_in)) || explorist;
+static void termdeinit() {
        if (is_file) {
-               icon_mapping = map330_icon_table;
-               mag_cleanse = m330_cleanse;
-               got_version = 1;
-               return;
-       } 
-
-       magfile_out = xfopen(portname, "w+b", MYNAME);
-       magfd = fileno(magfile_in);
-
-       tcgetattr(magfd, &orig_tio);
-       new_tio = orig_tio;
-       new_tio.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|
-               IGNCR|ICRNL|IXON);
-       new_tio.c_oflag &= ~OPOST;
-       new_tio.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
-       new_tio.c_cflag &= ~(CSIZE|PARENB);
-       new_tio.c_cflag |= CS8;
-       new_tio.c_cc[VTIME] = 10;
-       new_tio.c_cc[VMIN] = 0;
-
-       cfsetospeed(&new_tio, mkspeed(bitrate));
-       cfsetispeed(&new_tio, mkspeed(bitrate));
-       tcsetattr(magfd, TCSAFLUSH, &new_tio);
-}
-
-static void
-termdeinit()
-{
-       if (!is_file) {
-               tcsetattr(magfd, TCSANOW, &orig_tio);
+               fclose(magfile_h);
+               magfile_h = NULL;
+       } else {
+               gbser_deinit(serial_handle);
+               serial_handle = NULL;
        }
 }
 
-static char * 
-termread(char *ibuf, int size)
-{
-       return fgets(ibuf, size, magfile_in);
-}
-
-static void
-termwrite(char *obuf, int size)
-{
-       fwrite(obuf, size, 1, magfile_out);
-}
-#endif
-
 /*
  *  Arg tables are doubled up so that -? can output appropriate help
  */
@@ -861,38 +698,12 @@ mag_wr_init_common(const char *portname)
                wptcmtcnt_max = MAXCMTCT ;
        }
 
-#if __WIN32__
-       if (!terminit(portname, 1)) {
-               is_file = 1;
-       }
-#else
-       magfile_out = xfopen(portname, "w+b", MYNAME);
-       is_file =  (0 == strcmp(portname,"-")) || !isatty(fileno(magfile_out)) || explorist;
-#endif
+       terminit(portname, 1);
 
        if (!mkshort_handle) {
                mkshort_handle = mkshort_new_handle();
        }
-       if (is_file) {
-               magfile_out = xfopen(portname, "w+b", MYNAME);
-               icon_mapping = map330_icon_table;
-               mag_cleanse = m330_cleanse;
-               got_version = 1;
-       } else {
-               /*
-                *  This is a serial device.   The line has to be open for
-                *  reading and writing, so we let rd_init do the dirty work.
-                */
-               if (magfile_out) {
-                       fclose(magfile_out);
-               }
-#if __WIN32__
-               if (comport) {
-                       xCloseHandle(comport);
-               }
-#endif
-               mag_rd_init(portname);
-       }
+
        QUEUE_INIT(&rte_wpt_tmp);
 }
 
@@ -928,9 +739,6 @@ mag_deinit(void)
 {
        mag_handoff();
        termdeinit();
-       if(magfile_in)
-               fclose(magfile_in);
-       magfile_in = NULL;
        if(mkshort_handle)
                mkshort_del_handle(&mkshort_handle);
 
@@ -1240,8 +1048,8 @@ static void
 mag_read(void)
 {
        found_done = 0;
-
         if (global_opts.masked_objective & TRKDATAMASK) {
+                 magrxstate = mrs_handoff;
           if (!is_file) 
             mag_writemsg("PMGNCMD,TRACK,2");
           
@@ -1251,6 +1059,7 @@ mag_read(void)
         }
 
         if (global_opts.masked_objective & WPTDATAMASK) {
+                 magrxstate = mrs_handoff;
           if (!is_file) 
             mag_writemsg("PMGNCMD,WAYPOINT");
           
@@ -1260,6 +1069,7 @@ mag_read(void)
         }
 
         if (global_opts.masked_objective & RTEDATAMASK) {
+                 magrxstate = mrs_handoff;
           if (!is_file) {
             /* 
              * serial routes require waypoint & routes 
index 3748f751f5bfb2d6165ebe025cfd91c6d86336b9..538236d6f9ea21855dd53760b1fbae771b80b2c1 100644 (file)
@@ -1,21 +1,20 @@
-Microsoft Visual Studio Solution File, Format Version 8.00
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GPSBabel", "GPSBabel.vcproj", "{EB2609DB-5800-45B2-BCB2-06D72F389DCA}"
-       ProjectSection(ProjectDependencies) = postProject
-       EndProjectSection
-EndProject
-Global
-       GlobalSection(SolutionConfiguration) = preSolution
-               Debug = Debug
-               Release = Release
-       EndGlobalSection
-       GlobalSection(ProjectConfiguration) = postSolution
-               {EB2609DB-5800-45B2-BCB2-06D72F389DCA}.Debug.ActiveCfg = Debug|Win32
-               {EB2609DB-5800-45B2-BCB2-06D72F389DCA}.Debug.Build.0 = Debug|Win32
-               {EB2609DB-5800-45B2-BCB2-06D72F389DCA}.Release.ActiveCfg = Release|Win32
-               {EB2609DB-5800-45B2-BCB2-06D72F389DCA}.Release.Build.0 = Release|Win32
-       EndGlobalSection
-       GlobalSection(ExtensibilityGlobals) = postSolution
-       EndGlobalSection
-       GlobalSection(ExtensibilityAddIns) = postSolution
-       EndGlobalSection
-EndGlobal
+\r
+Microsoft Visual Studio Solution File, Format Version 9.00\r
+# Visual C++ Express 2005\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GPSBabel", "GPSBabel.vcproj", "{D81B30A1-C560-42BC-AE80-257FDDD484D8}"\r
+EndProject\r
+Global\r
+       GlobalSection(SolutionConfigurationPlatforms) = preSolution\r
+               Debug|Win32 = Debug|Win32\r
+               Release|Win32 = Release|Win32\r
+       EndGlobalSection\r
+       GlobalSection(ProjectConfigurationPlatforms) = postSolution\r
+               {D81B30A1-C560-42BC-AE80-257FDDD484D8}.Debug|Win32.ActiveCfg = Debug|Win32\r
+               {D81B30A1-C560-42BC-AE80-257FDDD484D8}.Debug|Win32.Build.0 = Debug|Win32\r
+               {D81B30A1-C560-42BC-AE80-257FDDD484D8}.Release|Win32.ActiveCfg = Release|Win32\r
+               {D81B30A1-C560-42BC-AE80-257FDDD484D8}.Release|Win32.Build.0 = Release|Win32\r
+       EndGlobalSection\r
+       GlobalSection(SolutionProperties) = preSolution\r
+               HideSolutionNode = FALSE\r
+       EndGlobalSection\r
+EndGlobal\r
index 2ec9b9b65e7c609fb1429c3731559269e6f47ce7..8fc9f48b1adece80f63ecdf04efc29453629d11a 100644 (file)
@@ -3,7 +3,7 @@
        ProjectType="Visual C++"\r
        Version="8.00"\r
        Name="GPSBabel"\r
-       ProjectGUID="{EB2609DB-5800-45B2-BCB2-06D72F389DCA}"\r
+       ProjectGUID="{D81B30A1-C560-42BC-AE80-257FDDD484D8}"\r
        >\r
        <Platforms>\r
                <Platform\r
@@ -45,7 +45,7 @@
                                Optimization="0"\r
                                InlineFunctionExpansion="1"\r
                                AdditionalIncludeDirectories="expat,..\coldsync;."\r
-                               PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;__WIN32__;VERSION=\&quot;1.2.1_beta01072004_msvc\&quot;;_CRT_SECURE_NO_DEPRECATE"\r
+                               PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;__WIN32__;VERSION=\&quot;1.3.0\&quot;;_CRT_SECURE_NO_DEPRECATE"\r
                                StringPooling="true"\r
                                RuntimeLibrary="0"\r
                                EnableFunctionLevelLinking="true"\r
                                Name="VCCLCompilerTool"\r
                                Optimization="0"\r
                                AdditionalIncludeDirectories="expat,..\coldsync;."\r
-                               PreprocessorDefinitions="WIN32;__WIN32__;_DEBUG;_CONSOLE;VERSION=\&quot;1.2.1_beta01072004_msvc\&quot;;_CRT_SECURE_NO_DEPRECATE"\r
+                               PreprocessorDefinitions="WIN32;__WIN32__;_DEBUG;_CONSOLE;VERSION=\&quot;1.3.0\&quot;;_CRT_SECURE_NO_DEPRECATE"\r
                                BasicRuntimeChecks="3"\r
                                RuntimeLibrary="1"\r
                                UsePrecompiledHeader="0"\r
                                RelativePath="..\garmin_tables.c"\r
                                >\r
                        </File>\r
+                       <File\r
+                               RelativePath="..\gbser.c"\r
+                               >\r
+                       </File>\r
                        <File\r
                                RelativePath="..\gbser_win.c"\r
                                >\r
                                        />\r
                                </FileConfiguration>\r
                        </File>\r
+                       <File\r
+                               RelativePath="..\textfile.c"\r
+                               >\r
+                       </File>\r
                        <File\r
                                RelativePath="..\tiger.c"\r
                                >\r
                                RelativePath="..\garmin_tables.h"\r
                                >\r
                        </File>\r
+                       <File\r
+                               RelativePath="..\gbser.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\gbser_private.h"\r
+                               >\r
+                       </File>\r
                        <File\r
                                RelativePath="..\grtcirc.h"\r
                                >\r
diff --git a/gpsbabel/reference/wbt-200.bin b/gpsbabel/reference/wbt-200.bin
new file mode 100644 (file)
index 0000000..a99d163
Binary files /dev/null and b/gpsbabel/reference/wbt-200.bin differ
diff --git a/gpsbabel/reference/wbt-200.gpx b/gpsbabel/reference/wbt-200.gpx
new file mode 100644 (file)
index 0000000..af01949
--- /dev/null
@@ -0,0 +1,1170 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gpx
+ version="1.0"
+creator="GPSBabel - http://www.gpsbabel.org"
+xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+xmlns="http://www.topografix.com/GPX/1/0"
+xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd">
+<bounds minlat="54.785117200" minlon ="-2.345095800" maxlat="54.787984800" maxlon="-2.342500800" />
+<trk>
+<trkseg>
+<trkpt lat="54.786918600" lon="-2.344296200">
+  <speed>0.556136</speed>
+  <name>WP0001</name>
+</trkpt>
+<trkpt lat="54.786917800" lon="-2.344315400">
+  <course>94.133018</course>
+  <speed>0.247108</speed>
+  <name>WP0002</name>
+</trkpt>
+<trkpt lat="54.786918500" lon="-2.344317400">
+  <course>58.742805</course>
+  <speed>0.026882</speed>
+  <name>WP0003</name>
+</trkpt>
+<trkpt lat="54.786921300" lon="-2.344313000">
+  <speed>0.085008</speed>
+  <name>WP0004</name>
+</trkpt>
+<trkpt lat="54.786923900" lon="-2.344310300">
+  <speed>0.065847</speed>
+  <name>WP0005</name>
+</trkpt>
+<trkpt lat="54.786926200" lon="-2.344311600">
+  <course>18.051563</course>
+  <speed>0.050291</speed>
+  <name>WP0006</name>
+</trkpt>
+<trkpt lat="54.786925900" lon="-2.344315900">
+  <course>96.898911</course>
+  <speed>0.053764</speed>
+  <name>WP0007</name>
+</trkpt>
+<trkpt lat="54.786924000" lon="-2.344320400">
+  <course>126.213005</course>
+  <speed>0.071123</speed>
+  <name>WP0008</name>
+</trkpt>
+<trkpt lat="54.786922100" lon="-2.344322900">
+  <course>142.812088</course>
+  <speed>0.050291</speed>
+  <name>WP0009</name>
+</trkpt>
+<trkpt lat="54.786919800" lon="-2.344325500">
+  <course>146.902481</course>
+  <speed>0.060110</speed>
+  <name>WP0010</name>
+</trkpt>
+<trkpt lat="54.786917100" lon="-2.344328300">
+  <course>149.121628</course>
+  <speed>0.068536</speed>
+  <name>WP0011</name>
+</trkpt>
+<trkpt lat="54.786913900" lon="-2.344332500">
+  <course>142.881119</course>
+  <speed>0.087107</speed>
+  <name>WP0012</name>
+</trkpt>
+<trkpt lat="54.786910500" lon="-2.344337100">
+  <course>142.041077</course>
+  <speed>0.095042</speed>
+  <name>WP0013</name>
+</trkpt>
+<trkpt lat="54.786907000" lon="-2.344341500">
+  <course>144.061920</course>
+  <speed>0.095042</speed>
+  <name>WP0014</name>
+</trkpt>
+<trkpt lat="54.786904000" lon="-2.344345500">
+  <course>142.445999</course>
+  <speed>0.082855</speed>
+  <name>WP0015</name>
+</trkpt>
+<trkpt lat="54.786901500" lon="-2.344348100">
+  <course>149.049561</course>
+  <speed>0.063043</speed>
+  <name>WP0016</name>
+</trkpt>
+<trkpt lat="54.786899100" lon="-2.344349500">
+  <course>161.409058</course>
+  <speed>0.057025</speed>
+  <name>WP0017</name>
+</trkpt>
+<trkpt lat="54.786896800" lon="-2.344349800">
+  <course>175.698807</course>
+  <speed>0.050291</speed>
+  <name>WP0018</name>
+</trkpt>
+<trkpt lat="54.786894700" lon="-2.344350500">
+  <course>169.120071</course>
+  <speed>0.050291</speed>
+  <name>WP0019</name>
+</trkpt>
+<trkpt lat="54.786893000" lon="-2.344352300">
+  <course>148.594345</course>
+  <speed>0.046561</speed>
+  <name>WP0020</name>
+</trkpt>
+<trkpt lat="54.786891400" lon="-2.344354300">
+  <course>144.216919</course>
+  <speed>0.042504</speed>
+  <name>WP0021</name>
+</trkpt>
+<trkpt lat="54.786889700" lon="-2.344355900">
+  <course>151.511292</course>
+  <speed>0.046561</speed>
+  <name>WP0022</name>
+</trkpt>
+<trkpt lat="54.786888400" lon="-2.344356700">
+  <course>160.463150</course>
+  <speed>0.026882</speed>
+  <name>WP0023</name>
+</trkpt>
+<trkpt lat="54.786887400" lon="-2.344356700">
+  <course>180.000000</course>
+  <speed>0.019008</speed>
+  <name>WP0024</name>
+</trkpt>
+<trkpt lat="54.786886600" lon="-2.344357000">
+  <course>167.798660</course>
+  <speed>0.000000</speed>
+  <name>WP0025</name>
+</trkpt>
+<trkpt lat="54.786886000" lon="-2.344357200">
+  <course>169.120071</course>
+  <speed>0.000000</speed>
+  <name>WP0026</name>
+</trkpt>
+<trkpt lat="54.786885400" lon="-2.344357400">
+  <course>169.120071</course>
+  <speed>0.000000</speed>
+  <name>WP0027</name>
+</trkpt>
+<trkpt lat="54.786884900" lon="-2.344356900">
+  <speed>0.000000</speed>
+  <name>WP0028</name>
+</trkpt>
+<trkpt lat="54.786884800" lon="-2.344356300">
+  <speed>0.000000</speed>
+  <name>WP0029</name>
+</trkpt>
+<trkpt lat="54.786877300" lon="-2.344355400">
+  <speed>0.167877</speed>
+  <name>WP0030</name>
+</trkpt>
+<trkpt lat="54.786875900" lon="-2.344370000">
+  <course>99.441742</course>
+  <speed>0.190083</speed>
+  <name>WP0031</name>
+</trkpt>
+<trkpt lat="54.786883500" lon="-2.344370000">
+  <course>0.000000</course>
+  <speed>0.170016</speed>
+  <name>WP0032</name>
+</trkpt>
+<trkpt lat="54.786881400" lon="-2.344368600">
+  <speed>0.053764</speed>
+  <name>WP0033</name>
+</trkpt>
+<trkpt lat="54.786878700" lon="-2.344367600">
+  <speed>0.063043</speed>
+  <name>WP0034</name>
+</trkpt>
+<trkpt lat="54.786876400" lon="-2.344365300">
+  <speed>0.057025</speed>
+  <name>WP0035</name>
+</trkpt>
+<trkpt lat="54.786877400" lon="-2.344361000">
+  <speed>0.057025</speed>
+  <name>WP0036</name>
+</trkpt>
+<trkpt lat="54.786939600" lon="-2.344332500">
+  <speed>1.432199</speed>
+  <name>WP0037</name>
+</trkpt>
+<trkpt lat="54.787010100" lon="-2.344387100">
+  <course>24.064192</course>
+  <speed>1.432560</speed>
+  <name>WP0038</name>
+</trkpt>
+<trkpt lat="54.787079800" lon="-2.344423000">
+  <course>16.541142</course>
+  <speed>1.618836</speed>
+  <name>WP0039</name>
+</trkpt>
+<trkpt lat="54.787159200" lon="-2.344461600">
+  <course>15.659211</course>
+  <speed>1.835854</speed>
+  <name>WP0040</name>
+</trkpt>
+<trkpt lat="54.787268800" lon="-2.344447800">
+  <speed>2.446469</speed>
+  <name>WP0041</name>
+</trkpt>
+<trkpt lat="54.787316800" lon="-2.344428600">
+  <speed>1.096733</speed>
+  <name>WP0042</name>
+</trkpt>
+<trkpt lat="54.787358600" lon="-2.344494600">
+  <course>42.315998</course>
+  <speed>1.258575</speed>
+  <name>WP0043</name>
+</trkpt>
+<trkpt lat="54.787398000" lon="-2.344587300">
+  <course>53.605648</course>
+  <speed>1.478501</speed>
+  <name>WP0044</name>
+</trkpt>
+<trkpt lat="54.787446000" lon="-2.344635900">
+  <course>30.277189</course>
+  <speed>1.237587</speed>
+  <name>WP0045</name>
+</trkpt>
+<trkpt lat="54.787489200" lon="-2.344701500">
+  <course>41.205185</course>
+  <speed>1.278231</speed>
+  <name>WP0046</name>
+</trkpt>
+<trkpt lat="54.787536100" lon="-2.344777300">
+  <course>42.981739</course>
+  <speed>1.427271</speed>
+  <name>WP0047</name>
+</trkpt>
+<trkpt lat="54.787576800" lon="-2.344848600">
+  <course>45.288700</course>
+  <speed>1.287806</speed>
+  <name>WP0048</name>
+</trkpt>
+<trkpt lat="54.787619000" lon="-2.344905600">
+  <course>37.912586</course>
+  <speed>1.190717</speed>
+  <name>WP0049</name>
+</trkpt>
+<trkpt lat="54.787654600" lon="-2.344976100">
+  <course>48.789742</course>
+  <speed>1.202943</speed>
+  <name>WP0050</name>
+</trkpt>
+<trkpt lat="54.787706000" lon="-2.345057400">
+  <course>42.365654</course>
+  <speed>1.548800</speed>
+  <name>WP0051</name>
+</trkpt>
+<trkpt lat="54.787704600" lon="-2.345093500">
+  <course>93.847755</course>
+  <speed>0.464052</speed>
+  <name>WP0052</name>
+</trkpt>
+<trkpt lat="54.787650000" lon="-2.345038300">
+  <speed>1.407002</speed>
+  <name>WP0053</name>
+</trkpt>
+<trkpt lat="54.787599300" lon="-2.344979500">
+  <speed>1.357865</speed>
+  <name>WP0054</name>
+</trkpt>
+<trkpt lat="54.787556600" lon="-2.344911400">
+  <speed>1.291588</speed>
+  <name>WP0055</name>
+</trkpt>
+<trkpt lat="54.787516700" lon="-2.344837400">
+  <speed>1.300648</speed>
+  <name>WP0056</name>
+</trkpt>
+<trkpt lat="54.787474300" lon="-2.344746700">
+  <speed>1.499009</speed>
+  <name>WP0057</name>
+</trkpt>
+<trkpt lat="54.787424700" lon="-2.344667700">
+  <speed>1.499370</speed>
+  <name>WP0058</name>
+</trkpt>
+<trkpt lat="54.787376900" lon="-2.344594500">
+  <speed>1.419783</speed>
+  <name>WP0059</name>
+</trkpt>
+<trkpt lat="54.787337800" lon="-2.344539200">
+  <speed>1.123262</speed>
+  <name>WP0060</name>
+</trkpt>
+<trkpt lat="54.787292000" lon="-2.344480900">
+  <speed>1.264875</speed>
+  <name>WP0061</name>
+</trkpt>
+<trkpt lat="54.787233600" lon="-2.344424200">
+  <speed>1.489943</speed>
+  <name>WP0062</name>
+</trkpt>
+<trkpt lat="54.787188100" lon="-2.344418700">
+  <speed>1.015479</speed>
+  <name>WP0063</name>
+</trkpt>
+<trkpt lat="54.787137900" lon="-2.344400400">
+  <speed>1.142083</speed>
+  <name>WP0064</name>
+</trkpt>
+<trkpt lat="54.787093100" lon="-2.344371800">
+  <speed>1.062938</speed>
+  <name>WP0065</name>
+</trkpt>
+<trkpt lat="54.787020700" lon="-2.344353100">
+  <speed>1.629736</speed>
+  <name>WP0066</name>
+</trkpt>
+<trkpt lat="54.786968300" lon="-2.344298000">
+  <speed>1.364369</speed>
+  <name>WP0067</name>
+</trkpt>
+<trkpt lat="54.786953700" lon="-2.344270600">
+  <speed>0.478995</speed>
+  <name>WP0068</name>
+</trkpt>
+<trkpt lat="54.786928100" lon="-2.344276200">
+  <course>172.810944</course>
+  <speed>0.574354</speed>
+  <name>WP0069</name>
+</trkpt>
+<trkpt lat="54.786947500" lon="-2.344239500">
+  <speed>0.638974</speed>
+  <name>WP0070</name>
+</trkpt>
+<trkpt lat="54.786874000" lon="-2.344211000">
+  <speed>1.676724</speed>
+  <name>WP0071</name>
+</trkpt>
+<trkpt lat="54.786889500" lon="-2.344175300">
+  <speed>0.573724</speed>
+  <name>WP0072</name>
+</trkpt>
+<trkpt lat="54.786905700" lon="-2.344154600">
+  <speed>0.448210</speed>
+  <name>WP0073</name>
+</trkpt>
+<trkpt lat="54.786887300" lon="-2.344152700">
+  <speed>0.410333</speed>
+  <name>WP0074</name>
+</trkpt>
+<trkpt lat="54.786967100" lon="-2.344200300">
+  <course>18.980536</course>
+  <speed>1.878750</speed>
+  <name>WP0075</name>
+</trkpt>
+<trkpt lat="54.786950400" lon="-2.344192500">
+  <speed>0.384889</speed>
+  <name>WP0076</name>
+</trkpt>
+<trkpt lat="54.786550600" lon="-2.344644800">
+  <course>146.881775</course>
+  <speed>10.627611</speed>
+  <name>WP0077</name>
+</trkpt>
+<trkpt lat="54.786423800" lon="-2.344905900">
+  <course>130.104218</course>
+  <speed>4.382399</speed>
+  <name>WP0078</name>
+</trkpt>
+<trkpt lat="54.786339400" lon="-2.344960500">
+  <course>159.542908</course>
+  <speed>2.005446</speed>
+  <name>WP0079</name>
+</trkpt>
+<trkpt lat="54.786129600" lon="-2.345095800">
+  <course>159.601395</course>
+  <speed>4.983410</speed>
+  <name>WP0080</name>
+</trkpt>
+<trkpt lat="54.786113000" lon="-2.345044900">
+  <speed>0.750528</speed>
+  <name>WP0081</name>
+</trkpt>
+<trkpt lat="54.786210000" lon="-2.344944400">
+  <speed>2.515643</speed>
+  <name>WP0082</name>
+</trkpt>
+<trkpt lat="54.786279400" lon="-2.344882300">
+  <speed>1.738717</speed>
+  <name>WP0083</name>
+</trkpt>
+<trkpt lat="54.786328600" lon="-2.344835700">
+  <speed>1.248053</speed>
+  <name>WP0084</name>
+</trkpt>
+<trkpt lat="54.786377000" lon="-2.344791200">
+  <speed>1.219648</speed>
+  <name>WP0085</name>
+</trkpt>
+<trkpt lat="54.786418000" lon="-2.344751600">
+  <speed>1.044767</speed>
+  <name>WP0086</name>
+</trkpt>
+<trkpt lat="54.786450500" lon="-2.344721800">
+  <speed>0.818463</speed>
+  <name>WP0087</name>
+</trkpt>
+<trkpt lat="54.786480100" lon="-2.344698000">
+  <speed>0.726058</speed>
+  <name>WP0088</name>
+</trkpt>
+<trkpt lat="54.786506500" lon="-2.344674100">
+  <speed>0.552369</speed>
+  <name>WP0089</name>
+</trkpt>
+<trkpt lat="54.786529400" lon="-2.344655300">
+  <speed>0.563878</speed>
+  <name>WP0090</name>
+</trkpt>
+<trkpt lat="54.786550300" lon="-2.344636900">
+  <speed>0.521951</speed>
+  <name>WP0091</name>
+</trkpt>
+<trkpt lat="54.786568700" lon="-2.344621400">
+  <speed>0.455804</speed>
+  <name>WP0092</name>
+</trkpt>
+<trkpt lat="54.786586200" lon="-2.344606000">
+  <speed>0.436778</speed>
+  <name>WP0093</name>
+</trkpt>
+<trkpt lat="54.786601800" lon="-2.344590800">
+  <speed>0.398269</speed>
+  <name>WP0094</name>
+</trkpt>
+<trkpt lat="54.786616000" lon="-2.344576900">
+  <speed>0.363154</speed>
+  <name>WP0095</name>
+</trkpt>
+<trkpt lat="54.786630100" lon="-2.344563400">
+  <speed>0.358144</speed>
+  <name>WP0096</name>
+</trkpt>
+<trkpt lat="54.786643500" lon="-2.344550500">
+  <speed>0.341092</speed>
+  <name>WP0097</name>
+</trkpt>
+<trkpt lat="54.786656400" lon="-2.344538400">
+  <speed>0.326479</speed>
+  <name>WP0098</name>
+</trkpt>
+<trkpt lat="54.786712800" lon="-2.344309700">
+  <speed>3.193230</speed>
+  <name>WP0099</name>
+</trkpt>
+<trkpt lat="54.786421700" lon="-2.344238300">
+  <speed>6.545493</speed>
+  <name>WP0100</name>
+</trkpt>
+<trkpt lat="54.786106300" lon="-2.344361100">
+  <course>167.346375</course>
+  <speed>7.196805</speed>
+  <name>WP0101</name>
+</trkpt>
+<trkpt lat="54.785876000" lon="-2.344429400">
+  <course>170.295609</course>
+  <speed>5.201792</speed>
+  <name>WP0102</name>
+</trkpt>
+<trkpt lat="54.785637300" lon="-2.344343800">
+  <speed>5.426836</speed>
+  <name>WP0103</name>
+</trkpt>
+<trkpt lat="54.785565000" lon="-2.344340400">
+  <speed>1.610220</speed>
+  <name>WP0104</name>
+</trkpt>
+<trkpt lat="54.785501100" lon="-2.344222400">
+  <speed>2.078176</speed>
+  <name>WP0105</name>
+</trkpt>
+<trkpt lat="54.785521000" lon="-2.344218800">
+  <speed>0.445379</speed>
+  <name>WP0106</name>
+</trkpt>
+<trkpt lat="54.785462300" lon="-2.344312400">
+  <course>137.402069</course>
+  <speed>1.775321</speed>
+  <name>WP0107</name>
+</trkpt>
+<trkpt lat="54.785276400" lon="-2.344252900">
+  <speed>4.208789</speed>
+  <name>WP0108</name>
+</trkpt>
+<trkpt lat="54.785179600" lon="-2.344116300">
+  <speed>2.778599</speed>
+  <name>WP0109</name>
+</trkpt>
+<trkpt lat="54.785117200" lon="-2.344006700">
+  <speed>1.977414</speed>
+  <name>WP0110</name>
+</trkpt>
+<trkpt lat="54.785218000" lon="-2.344177300">
+  <course>44.302502</course>
+  <speed>2.613213</speed>
+  <name>WP0111</name>
+</trkpt>
+<trkpt lat="54.785293900" lon="-2.344022300">
+  <speed>2.175488</speed>
+  <name>WP0112</name>
+</trkpt>
+<trkpt lat="54.785521400" lon="-2.344255100">
+  <course>30.543665</course>
+  <speed>5.881074</speed>
+  <name>WP0113</name>
+</trkpt>
+<trkpt lat="54.785671200" lon="-2.344188200">
+  <speed>3.443919</speed>
+  <name>WP0114</name>
+</trkpt>
+<trkpt lat="54.785927500" lon="-2.344016000">
+  <speed>6.119472</speed>
+  <name>WP0115</name>
+</trkpt>
+<trkpt lat="54.786170000" lon="-2.344095900">
+  <course>10.757442</course>
+  <speed>5.495544</speed>
+  <name>WP0116</name>
+</trkpt>
+<trkpt lat="54.786300400" lon="-2.344209100">
+  <course>26.591137</course>
+  <speed>3.246643</speed>
+  <name>WP0117</name>
+</trkpt>
+<trkpt lat="54.786436200" lon="-2.344380400">
+  <course>36.030758</course>
+  <speed>3.738654</speed>
+  <name>WP0118</name>
+</trkpt>
+<trkpt lat="54.786480200" lon="-2.344508600">
+  <course>59.238380</course>
+  <speed>1.915319</speed>
+  <name>WP0119</name>
+</trkpt>
+<trkpt lat="54.786509900" lon="-2.344486200">
+  <speed>0.720814</speed>
+  <name>WP0120</name>
+</trkpt>
+<trkpt lat="54.786535000" lon="-2.344489800">
+  <course>4.727779</course>
+  <speed>0.560343</speed>
+  <name>WP0121</name>
+</trkpt>
+<trkpt lat="54.786657100" lon="-2.344486000">
+  <speed>2.718789</speed>
+  <name>WP0122</name>
+</trkpt>
+<trkpt lat="54.786667100" lon="-2.344606900">
+  <course>81.836906</course>
+  <speed>1.306607</speed>
+  <name>WP0123</name>
+</trkpt>
+<trkpt lat="54.786687600" lon="-2.344578700">
+  <speed>0.582474</speed>
+  <name>WP0124</name>
+</trkpt>
+<trkpt lat="54.786707600" lon="-2.344565300">
+  <speed>0.477105</speed>
+  <name>WP0125</name>
+</trkpt>
+<trkpt lat="54.786715700" lon="-2.344577800">
+  <course>41.664234</course>
+  <speed>0.241189</speed>
+  <name>WP0126</name>
+</trkpt>
+<trkpt lat="54.786721300" lon="-2.344563900">
+  <speed>0.217560</speed>
+  <name>WP0127</name>
+</trkpt>
+<trkpt lat="54.786725800" lon="-2.344570900">
+  <course>41.891056</course>
+  <speed>0.133058</speed>
+  <name>WP0128</name>
+</trkpt>
+<trkpt lat="54.786755500" lon="-2.344623600">
+  <course>45.655930</course>
+  <speed>0.945843</speed>
+  <name>WP0129</name>
+</trkpt>
+<trkpt lat="54.786780200" lon="-2.344553000">
+  <speed>1.060215</speed>
+  <name>WP0130</name>
+</trkpt>
+<trkpt lat="54.786783900" lon="-2.344515800">
+  <speed>0.484619</speed>
+  <name>WP0131</name>
+</trkpt>
+<trkpt lat="54.786801900" lon="-2.344472200">
+  <speed>0.688249</speed>
+  <name>WP0132</name>
+</trkpt>
+<trkpt lat="54.786801800" lon="-2.344466100">
+  <speed>0.076033</speed>
+  <name>WP0133</name>
+</trkpt>
+<trkpt lat="54.786801300" lon="-2.344469800">
+  <course>103.189636</course>
+  <speed>0.046561</speed>
+  <name>WP0134</name>
+</trkpt>
+<trkpt lat="54.786803700" lon="-2.344463600">
+  <speed>0.096924</speed>
+  <name>WP0135</name>
+</trkpt>
+<trkpt lat="54.786806400" lon="-2.344457400">
+  <speed>0.098770</speed>
+  <name>WP0136</name>
+</trkpt>
+<trkpt lat="54.786808300" lon="-2.344454200">
+  <speed>0.057025</speed>
+  <name>WP0137</name>
+</trkpt>
+<trkpt lat="54.786809100" lon="-2.344457100">
+  <course>64.432991</course>
+  <speed>0.032923</speed>
+  <name>WP0138</name>
+</trkpt>
+<trkpt lat="54.786811800" lon="-2.344452300">
+  <speed>0.085008</speed>
+  <name>WP0139</name>
+</trkpt>
+<trkpt lat="54.786815500" lon="-2.344450300">
+  <speed>0.087107</speed>
+  <name>WP0140</name>
+</trkpt>
+<trkpt lat="54.786818200" lon="-2.344449000">
+  <speed>0.063043</speed>
+  <name>WP0141</name>
+</trkpt>
+<trkpt lat="54.786820300" lon="-2.344445100">
+  <speed>0.065847</speed>
+  <name>WP0142</name>
+</trkpt>
+<trkpt lat="54.786850700" lon="-2.344378700">
+  <speed>1.088465</speed>
+  <name>WP0143</name>
+</trkpt>
+<trkpt lat="54.786851100" lon="-2.344379500">
+  <course>49.070705</course>
+  <speed>0.000000</speed>
+  <name>WP0144</name>
+</trkpt>
+<trkpt lat="54.786851700" lon="-2.344372600">
+  <speed>0.091161</speed>
+  <name>WP0145</name>
+</trkpt>
+<trkpt lat="54.786850500" lon="-2.344371500">
+  <speed>0.038017</speed>
+  <name>WP0146</name>
+</trkpt>
+<trkpt lat="54.786851700" lon="-2.344361900">
+  <speed>0.127512</speed>
+  <name>WP0147</name>
+</trkpt>
+<trkpt lat="54.786852200" lon="-2.344355400">
+  <speed>0.085008</speed>
+  <name>WP0148</name>
+</trkpt>
+<trkpt lat="54.786855700" lon="-2.344344300">
+  <speed>0.162407</speed>
+  <name>WP0149</name>
+</trkpt>
+<trkpt lat="54.786856800" lon="-2.344342000">
+  <speed>0.038017</speed>
+  <name>WP0150</name>
+</trkpt>
+<trkpt lat="54.786854800" lon="-2.344344100">
+  <course>148.807159</course>
+  <speed>0.050291</speed>
+  <name>WP0151</name>
+</trkpt>
+<trkpt lat="54.786854300" lon="-2.344339000">
+  <speed>0.068536</speed>
+  <name>WP0152</name>
+</trkpt>
+<trkpt lat="54.786854300" lon="-2.344334100">
+  <speed>0.063043</speed>
+  <name>WP0153</name>
+</trkpt>
+<trkpt lat="54.786852500" lon="-2.344335600">
+  <course>154.334946</course>
+  <speed>0.046561</speed>
+  <name>WP0154</name>
+</trkpt>
+<trkpt lat="54.786852100" lon="-2.344330900">
+  <speed>0.060110</speed>
+  <name>WP0155</name>
+</trkpt>
+<trkpt lat="54.786854400" lon="-2.344318100">
+  <speed>0.172128</speed>
+  <name>WP0156</name>
+</trkpt>
+<trkpt lat="54.786856800" lon="-2.344306700">
+  <speed>0.155590</speed>
+  <name>WP0157</name>
+</trkpt>
+<trkpt lat="54.786858400" lon="-2.344299800">
+  <speed>0.093121</speed>
+  <name>WP0158</name>
+</trkpt>
+<trkpt lat="54.786858200" lon="-2.344299800">
+  <course>180.000000</course>
+  <speed>0.000000</speed>
+  <name>WP0159</name>
+</trkpt>
+<trkpt lat="54.786857500" lon="-2.344300600">
+  <course>146.615356</course>
+  <speed>0.019008</speed>
+  <name>WP0160</name>
+</trkpt>
+<trkpt lat="54.786856000" lon="-2.344304100">
+  <course>126.621498</course>
+  <speed>0.060110</speed>
+  <name>WP0161</name>
+</trkpt>
+<trkpt lat="54.786854600" lon="-2.344307000">
+  <course>129.936798</course>
+  <speed>0.046561</speed>
+  <name>WP0162</name>
+</trkpt>
+<trkpt lat="54.786853500" lon="-2.344309200">
+  <course>130.929291</course>
+  <speed>0.032923</speed>
+  <name>WP0163</name>
+</trkpt>
+<trkpt lat="54.786852700" lon="-2.344310700">
+  <course>132.766678</course>
+  <speed>0.000000</speed>
+  <name>WP0164</name>
+</trkpt>
+<trkpt lat="54.786851700" lon="-2.344313200">
+  <course>124.748962</course>
+  <speed>0.038017</speed>
+  <name>WP0165</name>
+</trkpt>
+<trkpt lat="54.786851100" lon="-2.344314400">
+  <course>130.929291</course>
+  <speed>0.026882</speed>
+  <name>WP0166</name>
+</trkpt>
+<trkpt lat="54.786850500" lon="-2.344316200">
+  <course>120.031418</course>
+  <speed>0.026882</speed>
+  <name>WP0167</name>
+</trkpt>
+<trkpt lat="54.786848600" lon="-2.344321700">
+  <course>120.925941</course>
+  <speed>0.082855</speed>
+  <name>WP0168</name>
+</trkpt>
+<trkpt lat="54.786846900" lon="-2.344326700">
+  <course>120.525436</course>
+  <speed>0.073619</speed>
+  <name>WP0169</name>
+</trkpt>
+<trkpt lat="54.786845300" lon="-2.344330700">
+  <course>124.748962</course>
+  <speed>0.063043</speed>
+  <name>WP0170</name>
+</trkpt>
+<trkpt lat="54.786843800" lon="-2.344334000">
+  <course>128.248459</course>
+  <speed>0.053764</speed>
+  <name>WP0171</name>
+</trkpt>
+<trkpt lat="54.786842400" lon="-2.344336100">
+  <course>139.142487</course>
+  <speed>0.038017</speed>
+  <name>WP0172</name>
+</trkpt>
+<trkpt lat="54.786841200" lon="-2.344337700">
+  <course>142.445953</course>
+  <speed>0.032923</speed>
+  <name>WP0173</name>
+</trkpt>
+<trkpt lat="54.786840300" lon="-2.344339500">
+  <course>130.929291</course>
+  <speed>0.026882</speed>
+  <name>WP0174</name>
+</trkpt>
+<trkpt lat="54.786840200" lon="-2.344340700">
+  <course>98.223473</course>
+  <speed>0.019008</speed>
+  <name>WP0175</name>
+</trkpt>
+<trkpt lat="54.786840400" lon="-2.344342600">
+  <course>79.654457</course>
+  <speed>0.019008</speed>
+  <name>WP0176</name>
+</trkpt>
+<trkpt lat="54.786840000" lon="-2.344345200">
+  <course>104.938911</course>
+  <speed>0.026882</speed>
+  <name>WP0177</name>
+</trkpt>
+<trkpt lat="54.786839700" lon="-2.344347800">
+  <course>101.315720</course>
+  <speed>0.019008</speed>
+  <name>WP0178</name>
+</trkpt>
+<trkpt lat="54.786839900" lon="-2.344349600">
+  <course>79.093140</course>
+  <speed>0.019008</speed>
+  <name>WP0179</name>
+</trkpt>
+<trkpt lat="54.786839900" lon="-2.344350700">
+  <course>90.000000</course>
+  <speed>0.019008</speed>
+  <name>WP0180</name>
+</trkpt>
+<trkpt lat="54.786840200" lon="-2.344350700">
+  <course>0.000000</course>
+  <speed>0.000000</speed>
+  <name>WP0181</name>
+</trkpt>
+<trkpt lat="54.786840900" lon="-2.344350000">
+  <speed>0.019008</speed>
+  <name>WP0182</name>
+</trkpt>
+<trkpt lat="54.786842300" lon="-2.344348300">
+  <speed>0.038017</speed>
+  <name>WP0183</name>
+</trkpt>
+<trkpt lat="54.786844000" lon="-2.344346000">
+  <speed>0.046561</speed>
+  <name>WP0184</name>
+</trkpt>
+<trkpt lat="54.786845900" lon="-2.344344300">
+  <speed>0.050291</speed>
+  <name>WP0185</name>
+</trkpt>
+<trkpt lat="54.786847900" lon="-2.344343000">
+  <speed>0.050291</speed>
+  <name>WP0186</name>
+</trkpt>
+<trkpt lat="54.786849500" lon="-2.344343100">
+  <course>2.063974</course>
+  <speed>0.032923</speed>
+  <name>WP0187</name>
+</trkpt>
+<trkpt lat="54.786850000" lon="-2.344344700">
+  <course>61.544453</course>
+  <speed>0.019008</speed>
+  <name>WP0188</name>
+</trkpt>
+<trkpt lat="54.786850500" lon="-2.344345600">
+  <course>46.065872</course>
+  <speed>0.019008</speed>
+  <name>WP0189</name>
+</trkpt>
+<trkpt lat="54.786851100" lon="-2.344345900">
+  <course>16.082796</course>
+  <speed>0.019008</speed>
+  <name>WP0190</name>
+</trkpt>
+<trkpt lat="54.786851900" lon="-2.344345400">
+  <speed>0.000000</speed>
+  <name>WP0191</name>
+</trkpt>
+<trkpt lat="54.786852700" lon="-2.344344700">
+  <speed>0.000000</speed>
+  <name>WP0192</name>
+</trkpt>
+<trkpt lat="54.786853500" lon="-2.344343600">
+  <speed>0.000000</speed>
+  <name>WP0193</name>
+</trkpt>
+<trkpt lat="54.786854300" lon="-2.344342300">
+  <speed>0.019008</speed>
+  <name>WP0194</name>
+</trkpt>
+<trkpt lat="54.786855100" lon="-2.344340600">
+  <speed>0.026882</speed>
+  <name>WP0195</name>
+</trkpt>
+<trkpt lat="54.786856200" lon="-2.344338400">
+  <speed>0.038017</speed>
+  <name>WP0196</name>
+</trkpt>
+<trkpt lat="54.786857000" lon="-2.344336600">
+  <speed>0.026882</speed>
+  <name>WP0197</name>
+</trkpt>
+<trkpt lat="54.786858200" lon="-2.344334500">
+  <speed>0.038017</speed>
+  <name>WP0198</name>
+</trkpt>
+<trkpt lat="54.786859900" lon="-2.344332700">
+  <speed>0.042504</speed>
+  <name>WP0199</name>
+</trkpt>
+<trkpt lat="54.786862100" lon="-2.344330500">
+  <speed>0.053764</speed>
+  <name>WP0200</name>
+</trkpt>
+<trkpt lat="54.786864100" lon="-2.344328100">
+  <speed>0.053764</speed>
+  <name>WP0201</name>
+</trkpt>
+<trkpt lat="54.787010400" lon="-2.344104300">
+  <speed>4.343226</speed>
+  <name>WP0202</name>
+</trkpt>
+<trkpt lat="54.787229300" lon="-2.343736200">
+  <speed>6.788423</speed>
+  <name>WP0203</name>
+</trkpt>
+<trkpt lat="54.787421800" lon="-2.343407600">
+  <speed>6.013606</speed>
+  <name>WP0204</name>
+</trkpt>
+<trkpt lat="54.787607100" lon="-2.343151200">
+  <speed>5.277713</speed>
+  <name>WP0205</name>
+</trkpt>
+<trkpt lat="54.787730000" lon="-2.342980300">
+  <speed>3.507230</speed>
+  <name>WP0206</name>
+</trkpt>
+<trkpt lat="54.787984800" lon="-2.342500800">
+  <speed>8.370941</speed>
+  <name>WP0207</name>
+</trkpt>
+<trkpt lat="54.787651300" lon="-2.342851000">
+  <course>148.805801</course>
+  <speed>8.679982</speed>
+  <name>WP0208</name>
+</trkpt>
+<trkpt lat="54.787296200" lon="-2.343383500">
+  <course>139.150696</course>
+  <speed>10.451539</speed>
+  <name>WP0209</name>
+</trkpt>
+<trkpt lat="54.787039700" lon="-2.343788000">
+  <course>137.718918</course>
+  <speed>7.718669</speed>
+  <name>WP0210</name>
+</trkpt>
+<trkpt lat="54.786803800" lon="-2.344073800">
+  <course>145.062073</course>
+  <speed>6.406710</speed>
+  <name>WP0211</name>
+</trkpt>
+<trkpt lat="54.786802300" lon="-2.344003800">
+  <speed>0.899237</speed>
+  <name>WP0212</name>
+</trkpt>
+<trkpt lat="54.786616200" lon="-2.344166600">
+  <course>153.232285</course>
+  <speed>4.640603</speed>
+  <name>WP0213</name>
+</trkpt>
+<trkpt lat="54.786540500" lon="-2.344416600">
+  <course>117.705025</course>
+  <speed>3.625063</speed>
+  <name>WP0214</name>
+</trkpt>
+<trkpt lat="54.786501700" lon="-2.344660800">
+  <course>105.405190</course>
+  <speed>3.251814</speed>
+  <name>WP0215</name>
+</trkpt>
+<trkpt lat="54.786513700" lon="-2.344705000">
+  <course>64.787437</course>
+  <speed>0.627275</speed>
+  <name>WP0216</name>
+</trkpt>
+<trkpt lat="54.786760100" lon="-2.344360500">
+  <speed>7.046580</speed>
+  <name>WP0217</name>
+</trkpt>
+<trkpt lat="54.787114900" lon="-2.343841500">
+  <speed>10.333961</speed>
+  <name>WP0218</name>
+</trkpt>
+<trkpt lat="54.786823000" lon="-2.344343500">
+  <course>135.240036</course>
+  <speed>9.152427</speed>
+  <name>WP0219</name>
+</trkpt>
+<trkpt lat="54.786727900" lon="-2.344510000">
+  <course>134.727890</course>
+  <speed>3.008605</speed>
+  <name>WP0220</name>
+</trkpt>
+<trkpt lat="54.786797300" lon="-2.344411700">
+  <speed>1.994969</speed>
+  <name>WP0221</name>
+</trkpt>
+<trkpt lat="54.786798000" lon="-2.344388300">
+  <speed>0.300548</speed>
+  <name>WP0222</name>
+</trkpt>
+<trkpt lat="54.787078000" lon="-2.343880200">
+  <speed>9.022692</speed>
+  <name>WP0223</name>
+</trkpt>
+<trkpt lat="54.787064400" lon="-2.343910000">
+  <course>128.360611</course>
+  <speed>0.487963</speed>
+  <name>WP0224</name>
+</trkpt>
+<trkpt lat="54.787059700" lon="-2.343934800">
+  <course>108.194084</course>
+  <speed>0.335216</speed>
+  <name>WP0225</name>
+</trkpt>
+<trkpt lat="54.787079300" lon="-2.343931200">
+  <speed>0.438841</speed>
+  <name>WP0226</name>
+</trkpt>
+<trkpt lat="54.787085100" lon="-2.343997400">
+  <course>81.360321</course>
+  <speed>0.859589</speed>
+  <name>WP0227</name>
+</trkpt>
+<trkpt lat="54.787090000" lon="-2.343998400">
+  <course>6.711519</course>
+  <speed>0.109195</speed>
+  <name>WP0228</name>
+</trkpt>
+<trkpt lat="54.787363500" lon="-2.343572600">
+  <speed>8.182794</speed>
+  <name>WP0229</name>
+</trkpt>
+<trkpt lat="54.787468300" lon="-2.343368600">
+  <speed>3.507539</speed>
+  <name>WP0230</name>
+</trkpt>
+<trkpt lat="54.787640700" lon="-2.343024900">
+  <speed>5.848113</speed>
+  <name>WP0231</name>
+</trkpt>
+<trkpt lat="54.787859200" lon="-2.342704200">
+  <speed>6.372952</speed>
+  <name>WP0232</name>
+</trkpt>
+<trkpt lat="54.787842200" lon="-2.342921800">
+  <course>97.715996</course>
+  <speed>2.818878</speed>
+  <name>WP0233</name>
+</trkpt>
+<trkpt lat="54.787355300" lon="-2.343799700">
+  <course>133.886002</course>
+  <speed>15.637352</speed>
+  <name>WP0234</name>
+</trkpt>
+<trkpt lat="54.787075000" lon="-2.344206800">
+  <course>140.055038</course>
+  <speed>8.139894</speed>
+  <name>WP0235</name>
+</trkpt>
+<trkpt lat="54.787010700" lon="-2.344363000">
+  <course>125.523293</course>
+  <speed>2.463761</speed>
+  <name>WP0236</name>
+</trkpt>
+<trkpt lat="54.786777100" lon="-2.344770400">
+  <course>134.839066</course>
+  <speed>7.375820</speed>
+  <name>WP0237</name>
+</trkpt>
+<trkpt lat="54.786630800" lon="-2.344906200">
+  <course>151.842575</course>
+  <speed>3.694468</speed>
+  <name>WP0238</name>
+</trkpt>
+<trkpt lat="54.786659200" lon="-2.344849700">
+  <speed>0.962317</speed>
+  <name>WP0239</name>
+</trkpt>
+<trkpt lat="54.786524100" lon="-2.345018400">
+  <course>144.244812</course>
+  <speed>3.706478</speed>
+  <name>WP0240</name>
+</trkpt>
+<trkpt lat="54.786864600" lon="-2.344376200">
+  <speed>11.200036</speed>
+  <name>WP0241</name>
+</trkpt>
+<trkpt lat="54.787250300" lon="-2.343633900">
+  <speed>12.827707</speed>
+  <name>WP0242</name>
+</trkpt>
+<trkpt lat="54.787807600" lon="-2.342578800">
+  <speed>18.368883</speed>
+  <name>WP0243</name>
+</trkpt>
+<trkpt lat="54.787178600" lon="-2.344040000">
+  <course>126.742546</course>
+  <speed>19.507580</speed>
+  <name>WP0244</name>
+</trkpt>
+<trkpt lat="54.786722100" lon="-2.344530500">
+  <course>148.218903</course>
+  <speed>7.472546</speed>
+  <name>WP0245</name>
+</trkpt>
+<trkpt lat="54.786872300" lon="-2.344456200">
+  <speed>2.483881</speed>
+  <name>WP0246</name>
+</trkpt>
+<trkpt lat="54.786790900" lon="-2.344473100">
+  <course>173.173264</course>
+  <speed>1.303782</speed>
+  <name>WP0247</name>
+</trkpt>
+<trkpt lat="54.786866200" lon="-2.344096600">
+  <speed>1.967664</speed>
+  <name>WP0248</name>
+</trkpt>
+<trkpt lat="54.786926000" lon="-2.344155900">
+  <course>29.760809</course>
+  <speed>1.533561</speed>
+  <name>WP0249</name>
+</trkpt>
+<trkpt lat="54.787223500" lon="-2.343181500">
+  <speed>14.154443</speed>
+  <name>WP0250</name>
+</trkpt>
+<trkpt lat="54.787151700" lon="-2.343740700">
+  <course>102.553291</course>
+  <speed>7.354651</speed>
+  <name>WP0251</name>
+</trkpt>
+<trkpt lat="54.787281000" lon="-2.343534000">
+  <speed>3.915116</speed>
+  <name>WP0252</name>
+</trkpt>
+<trkpt lat="54.787392100" lon="-2.343651100">
+  <course>31.289225</course>
+  <speed>2.894576</speed>
+  <name>WP0253</name>
+</trkpt>
+<trkpt lat="54.787738600" lon="-2.342950500">
+  <speed>11.849266</speed>
+  <name>WP0254</name>
+</trkpt>
+<trkpt lat="54.787550500" lon="-2.343047500">
+  <course>163.440262</course>
+  <speed>3.120760</speed>
+  <name>WP0255</name>
+</trkpt>
+<trkpt lat="54.786874100" lon="-2.344481700">
+  <course>129.279663</course>
+  <speed>2.973259</speed>
+  <name>WP0256</name>
+</trkpt>
+<trkpt lat="54.787425000" lon="-2.342911400">
+  <speed>23.597078</speed>
+  <name>WP0257</name>
+</trkpt>
+<trkpt lat="54.787020700" lon="-2.344089400">
+  <course>120.761200</course>
+  <speed>1.060176</speed>
+  <name>WP0258</name>
+</trkpt>
+<trkpt lat="54.787111400" lon="-2.344023100">
+  <speed>0.116564</speed>
+  <name>WP0259</name>
+</trkpt>
+</trkseg>
+</trk>
+</gpx>
index 8d43ce72ad829516858158628d4892777ab57ac4..5caf0aaa830cef55baf03b9032400c7c6db830c7 100755 (executable)
@@ -1074,5 +1074,13 @@ compare ${TMPDIR}/waypoints.gpssim ${REFERENCE}
 ${PNAME} -i gpx -f ${REFERENCE}/track/tracks.gpx -o gpssim -F ${TMPDIR}/tracks.gpssim
 compare ${TMPDIR}/tracks.gpssim ${REFERENCE}/track
 
+#
+# WBT-200 tests
+#
+rm -f ${TMPDIR}/wbt-200.*
+${PNAME} -i wbt-bin -f ${REFERENCE}/wbt-200.bin -o gpx -F ${TMPDIR}/wbt-200.gpx
+# Remove the timestamp
+grep -v time <${TMPDIR}/wbt-200.gpx >${TMPDIR}/wbt-200.gpx2
+compare ${TMPDIR}/wbt-200.gpx2 ${REFERENCE}/wbt-200.gpx
 
 exit 0
index c9ae47ad8518303f1652bfd8699ef4f000569f55..5864f74c7d99b64696668584a9ad75e603398160 100644 (file)
@@ -106,7 +106,8 @@ extern ff_vecs_t vitosmt_vecs;
 extern ff_vecs_t wfff_xml_vecs;
 extern ff_vecs_t xcsv_vecs;
 extern ff_vecs_t yahoo_vecs;
-extern ff_vecs_t wbt_vecs;
+extern ff_vecs_t wbt_svecs;
+extern ff_vecs_t wbt_fvecs;
 
 static
 vecs_t vec_list[] = {
@@ -382,9 +383,15 @@ vecs_t vec_list[] = {
                 NULL
         },
         {
-                &wbt_vecs,
+                &wbt_svecs,
                 "wbt",
                 "Wintec WBT-100/200 GPS Download",
+                "bin"
+        },
+        {
+                &wbt_fvecs,
+                "wbt-bin",
+                "Wintec WBT-100/200 Binary file format",
                 NULL
         },
         {
index 585cfd75f2709459f1df0451ea30786b0b3dcab6..685031bf543f0d1f444a0e1f00f1f37039e114c7 100644 (file)
  */
 
 #include "defs.h"
-#include "jeeps/gpsserial.h"
+#include "gbser.h"
 #include "grtcirc.h"
 #include <errno.h>
 
+#define BAUD        9600
+#define TIMEOUT     1500
+
 /*
     A conversation looks like this
 
     << $PFST,NORMAL,*02
 */
 
-static gpsdevh *fd;
+/*static gpsdevh *fd;*/
+static void *fd;
+static FILE *fl;
 static char *port;
 static char *erase;
 
 #define MYNAME      "WBT-100/200"
-#define PRESTRKNAME "PRESALTTRK"
 #define NL          "\x0D\x0A"
 
+struct read_state {
+       route_head      *route_head; 
+    double      plat, plon;     /* previous point */
+    time_t      ptim;
+    unsigned    wpn;
+};
+
 /* Number of lines to skip while waiting for an ACK from a command. I've seen
  * conversations with up to 30 lines of cruft before the response so 50 isn't
  * too crazy.
@@ -62,92 +73,63 @@ static void db(int l, const char *msg, ...) {
     va_end(ap);
 }
 
-/* Read a single character from the serial port. Kind of gross but we do
- * it like this so we can use Jeeps (which doesn't have an equivalent
- * function). Returns -1 if no char is available, -2 on error or the
- * retrieved char.
- */
-static int rd_char() {
-    if (GPS_Serial_Chars_Ready(fd)) {
-        unsigned char c;
-        if (GPS_Serial_Read(fd, &c, 1) != 1) {
-            return -2;
-        } else {
-            return c;
-        }
-    } else {
-        return -1;
-    }
-}
-
-/* Blocking version of above. It would be nicer to use select on the fd
- * rather than spinning in a loop here - but we're trying to stay platform
- * independent.
- */
-static int rd_char_b() {
-    int c = rd_char();
-    while (c == -1) {
-        c = rd_char();
+static void rd_drain() {
+    if (gbser_flush(fd)) {
+        fatal(MYNAME ": Comm error\n");
     }
-    
-    return c;
 }
 
-/* Swallow any pending output from GPS */
-
-static int rd_drain() {
-    int c = rd_char();
-    while (c >= 0) {
-        c = rd_char();
+static void rd_line(char *buf, int len) {
+    int rc;
+    if (rc = gbser_read_line(fd, buf, len, TIMEOUT, 0x0A, 0x0D), rc != gbser_OK) {
+        fatal(MYNAME ": Read error (%d)\n", rc);
     }
-    
-    return c == -1 ? 0 : c;
 }
 
-/* Read a line (up to 0x0A). Carriage returns are filtered out. Always tries to
- * read an entire line but discards any characters beyond len (because we're
- * only ever interested in fairly short lines.
- *
- * Returns the number of characters read or -2 on error. The buffer will contain
- * the (possibly truncated) string without any line terminator characters. The
- * buffer will always be null terminated.
- */
-static int rd_line(char *buf, int len) {
-    int c, pos = 0, nr = 0;
-    c = rd_char_b();
-    while (c >= 0 && c != 0x0A) {
-        nr++;
-        if (c != 0x0D && pos < len-1) {
-            buf[pos++] = (unsigned char) c;
-        }
-        c = rd_char_b();
+static void wr_cmd(const char *cmd) {
+    int rc;
+    db(3, "Sending: %s\n", cmd);
+    if (rc = gbser_print(fd, cmd), gbser_OK != rc) {
+        fatal(MYNAME ": Write error (%d)\n", rc);
     }
-    
-    buf[pos] = '\0';
-    
-    return c < 0 ? c : nr;
-}
-
-static int wr_cmd(const char *cmd) {
-    return GPS_Serial_Write(fd, cmd, strlen(cmd));
 }
 
 static void rd_init(const char *fname) {
     port = xstrdup(fname);
 
     db(1, "Opening port...\n");
-    if (!GPS_Serial_On(port, &fd)) {
-        fatal(MYNAME ": Can't initialise port '%s'\n", port);
+    if ((fd = gbser_init(port), NULL == fd) ||
+        gbser_set_port(fd, BAUD, 8, 0, 1)) {
+        fatal(MYNAME ": Can't initialise port \"%s\"\n", port);
     }
 }
 
 static void rd_deinit(void) {
     db(1, "Closing port...\n");
-    if (!GPS_Serial_Off(fd)) {
-        fatal(MYNAME ": Can't shut down port '%s'\n", port);
+    gbser_deinit(fd);
+    fd = NULL;
+    xfree(port);
+}
+
+static void rd_buf(void *buf, int len) {
+    int rc;
+    if (rc = gbser_read_wait(fd, buf, len, TIMEOUT), rc < 0) {
+        fatal(MYNAME ": Read error (%d)\n", rc);
+    } else if (rc < len) {
+        fatal(MYNAME ": Read timout\n");
     }
+}
 
-    xfree(port);
+static void file_init(const char *fname) {
+    db(1, "Opening file...\n");
+    if (fl = fopen(fname, "rb"), NULL == fl) {
+        fatal(MYNAME ": Can't open file '%s'\n", fname);
+    }
+}
+
+static void file_deinit(void) {
+    db(1, "Closing file...\n");
+    fclose(fl);
 }
 
 static int starts_with(const char *buf, const char *pat) {
@@ -159,12 +141,9 @@ static int starts_with(const char *buf, const char *pat) {
  */
  
 static void do_cmd(const char *cmd, const char *expect, char *buf, int len) {
-    int rc, try;
-
-    if (rd_drain() < 0) {
-        fatal(MYNAME ": Read error\n");
-    }
+    int try;
 
+    rd_drain();
     wr_cmd(cmd); 
     wr_cmd(NL);
 
@@ -175,9 +154,7 @@ static void do_cmd(const char *cmd, const char *expect, char *buf, int len) {
      * middle of an NMEA sentence when we start listening.
      */
     for (try = 0; try < RETRIES; try++) {
-        if (rc = rd_line(buf, len), rc < 0) {
-            fatal(MYNAME ": Read error\n");
-        }
+        rd_line(buf, len);
         if (starts_with(buf, expect)) {
             db(2, "Got: %s\n", buf);
             return;
@@ -192,16 +169,80 @@ static void do_simple(const char *cmd, char *buf, int len) {
     do_cmd(cmd, cmd, buf, len);
 }
 
-static void rd_buf(void *buf, int len) {
-    char *bp = buf;
+static void data_chunk(struct read_state *st, const void *buf) {
+    char        wp_name[20];
+    gbuint32    tim;
+    double      lat, lon;
+    struct tm   t;
+    time_t      rtim;
+       waypoint        *wpt        = NULL;
+       const char *bp = buf;
     
-    while (len > 0) {
-        int rc = GPS_Serial_Read(fd, bp, len);
-        if (rc < 0) {
-            fatal(MYNAME ": Read error\n");
-        }
-        len -= rc;
-        bp  += rc;
+    tim = le_read32(bp + 0);
+    
+    lat = (double) ((gbint32) le_read32(bp + 4)) / 10000000;
+    lon = (double) ((gbint32) le_read32(bp + 8)) / 10000000;
+    
+    t.tm_sec    = ((tim >>  0) & 0x3F);
+    t.tm_min    = ((tim >>  6) & 0x3F);
+    t.tm_hour   = ((tim >> 12) & 0x1F);
+    t.tm_mday   = ((tim >> 17) & 0x1F);
+    t.tm_mon    = ((tim >> 22) & 0x0F) - 1;
+    t.tm_year   = ((tim >> 26) & 0x3F) + 100;
+    
+    rtim = mkgmtime(&t);
+
+    if (lat >= 100) {
+        /* Start new track */
+        lat -= 100;
+        st->route_head = NULL;
+    } else {
+               wpt = waypt_new();
+       
+               wpt->latitude       = lat;;
+               wpt->longitude      = lon;
+               wpt->creation_time  = rtim;
+               wpt->centiseconds   = 0;
+       
+               /* OK to reuse buffer now */
+               sprintf(wp_name, "WP%04d", ++st->wpn);
+               wpt->shortname      = xstrdup(wp_name);
+               
+               wpt->speed          = radtometers(
+                                   gcdist(RAD(st->plat), RAD(st->plon), 
+                                          RAD(lat), RAD(lon))) /
+                                     (rtim - st->ptim);
+               wpt->course         = DEG(heading(RAD(st->plat), RAD(st->plon),
+                                             RAD(lat), RAD(lon)));
+               wpt->pdop               = 0;
+               wpt->fix                    = fix_unknown;
+
+               if (NULL == st->route_head)     {
+                   db(1, "New Track\n");
+                       st->route_head = route_head_alloc();
+                       track_add_head(st->route_head);
+               }
+
+               track_add_wpt(st->route_head, wpt);
+    }
+       
+       st->ptim = rtim;
+       st->plat = lat;
+       st->plon = lon;
+}
+
+static void file_read(void) {
+    char                buf[12];
+    int                 rc;
+    struct read_state   st;
+    
+    st.route_head = NULL;
+    st.wpn = 0;
+    
+    rc = fread(buf, sizeof(buf), 1, fl);
+    while (rc == 1) {
+        data_chunk(&st, buf);
+        rc = fread(buf, sizeof(buf), 1, fl);
     }
 }
 
@@ -210,15 +251,12 @@ static void data_read(void) {
      * Actually, it's OK because rd_line can read arbitrarily
      * long lines returning only the first N characters
      */
-    char        line_buf[100];
-    int         count, d;
-    gbuint32    tim, ptim = 0;
-    double      lat, lon;
-    double      plat = 0, plon = 0;     /* previous point */
-    struct tm   t;
-    time_t      rtim;
-       route_head      *route_head = NULL; 
-       waypoint        *wpt        = NULL;
+    char                line_buf[100];
+    int                 count, d;
+    struct read_state   st;
+    
+    st.route_head = NULL;
+    st.wpn = 0;
 
     do_simple("$PFST,FIRMWAREVERSION", line_buf, sizeof(line_buf));
     do_simple("$PFST,NORMAL",          line_buf, sizeof(line_buf));
@@ -230,61 +268,11 @@ static void data_read(void) {
     if (count == 0x10000) {
         count = 0;
     }
+    
     db(1, "Reading %d data\n", count);
     for (d = 0; d < count; d++) {
         rd_buf(line_buf, 12);       /* twelve byte record */
-        tim = le_read32(line_buf + 0);
-        
-        lat = (double) ((gbint32) le_read32(line_buf + 4)) / 10000000;
-        lon = (double) ((gbint32) le_read32(line_buf + 8)) / 10000000;
-        
-        t.tm_sec    = ((tim >>  0) & 0x3F);
-        t.tm_min    = ((tim >>  6) & 0x3F);
-        t.tm_hour   = ((tim >> 12) & 0x1F);
-        t.tm_mday   = ((tim >> 17) & 0x1F);
-        t.tm_mon    = ((tim >> 22) & 0x0F) - 1;
-        t.tm_year   = ((tim >> 26) & 0x3F) + 100;
-        
-        rtim = mkgmtime(&t);
-
-        if (lat >= 100) {
-            /* Start new track */
-            lat -= 100;
-            route_head = NULL;
-        } else {
-               wpt = waypt_new();
-               
-               wpt->latitude       = lat;;
-               wpt->longitude      = lon;
-               wpt->creation_time  = rtim;
-               wpt->centiseconds   = 0;
-               
-               /* OK to reuse buffer now */
-               sprintf(line_buf, "WP%04d", d + 1);
-               wpt->shortname      = xstrdup(line_buf);
-               
-                       wpt->speed          = radtometers(
-                                           gcdist(RAD(plat), RAD(plon), 
-                                                  RAD(lat),  RAD(lon))) /
-                                             (rtim - ptim);
-                       wpt->course         = DEG(heading(RAD(plat), RAD(plon),
-                                                     RAD(lat),  RAD(lon)));
-               wpt->pdop               = 0;
-               wpt->fix                    = fix_unknown;
-               wpt->sat            = 0;
-
-               if (NULL == route_head) {
-                   db(1, "New Track\n");
-                       route_head = route_head_alloc();
-                       track_add_head(route_head);
-               }
-
-               track_add_wpt(route_head, wpt);
-        }
-               
-               ptim = rtim;
-               plat = lat;
-               plon = lon;
+        data_chunk(&st, line_buf);
     }
     
     /* Erase data? */
@@ -307,13 +295,13 @@ static void data_read(void) {
     
 }
 
-static arglist_t wbt_args[] = {
+static arglist_t wbt_sargs[] = {
     { "erase", &erase, "Erase device data after download", 
         "0", ARGTYPE_BOOL, ARG_NOMINMAX },
     ARG_TERMINATOR
 };
 
-ff_vecs_t wbt_vecs = {
+ff_vecs_t wbt_svecs = {
     ff_type_serial,
     { ff_cap_none, ff_cap_read, ff_cap_none },
     rd_init,
@@ -323,6 +311,24 @@ ff_vecs_t wbt_vecs = {
     data_read,
     NULL,
     NULL, 
-    wbt_args,
+    wbt_sargs,
+    CET_CHARSET_UTF8, 1         /* master process: don't convert anything | CET-REVIEW */
+};
+
+static arglist_t wbt_fargs[] = {
+    ARG_TERMINATOR
+};
+
+ff_vecs_t wbt_fvecs = {
+    ff_type_file,
+    { ff_cap_none, ff_cap_read, ff_cap_none },
+    file_init,
+    NULL,
+    file_deinit,
+    NULL,
+    file_read,
+    NULL,
+    NULL, 
+    wbt_fargs,
     CET_CHARSET_UTF8, 1         /* master process: don't convert anything | CET-REVIEW */
 };